it-swarm.it

Perché utilizzare il tipo di dati geografia di SQL Server 2008?

Sto ridisegnando un database dei clienti e una delle nuove informazioni che vorrei archiviare insieme ai campi degli indirizzi standard (via, città, ecc.) È la posizione geografica dell'indirizzo. L'unico caso d'uso che ho in mente è quello di consentire agli utenti di mappare le coordinate su Google Maps quando l'indirizzo non può essere trovato altrimenti, cosa che spesso accade quando l'area è appena sviluppata o si trova in una posizione remota/rurale.

La mia prima inclinazione era quella di memorizzare latitudine e longitudine come valori decimali, ma poi ho ricordato che SQL Server 2008 R2 ha un tipo di dati geography. Non ho assolutamente alcuna esperienza con geography e dalla mia ricerca iniziale sembra essere eccessivo per il mio scenario.

Ad esempio, per lavorare con latitudine e longitudine memorizzate come decimal(7,4), posso fare questo:

insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest

ma con geography, farei questo:

insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest

Sebbene non sia che molto più complicato, perché aggiungere complessità se non è necessario?

Prima di abbandonare l'idea di utilizzare geography, c'è qualcosa che dovrei considerare? Sarebbe più veloce cercare una posizione utilizzando un indice spaziale rispetto all'indicizzazione dei campi Latitudine e Longitudine? Ci sono vantaggi nell'uso di geography di cui non sono a conoscenza? Oppure, sul rovescio della medaglia, ci sono avvertimenti che dovrei sapere che mi scoraggerebbero dall'usare geography?


Aggiornare

@Erik Philips ha mostrato la possibilità di effettuare ricerche di prossimità con geography, il che è molto interessante.

D'altra parte, un test rapido sta dimostrando che un semplice select per ottenere latitudine e longitudine è significativamente più lento quando si usa geography (dettagli sotto). e un commento su risposta accettata a un altro SO domanda su geography mi fa stare attenti:

@SaphuA Prego. Come sidenote sii MOLTO attento a utilizzare un indice spaziale su una colonna di tipo di dati GEOGRAFIA nullable. Ci sono alcuni seri problemi di prestazioni, quindi rendi la colonna GEOGRAFIA non annullabile anche se devi rimodellare lo schema. - Tomas, 18 giugno alle 11:18

Tutto sommato, soppesando la probabilità di effettuare ricerche di prossimità rispetto al compromesso in termini di prestazioni e complessità, ho deciso di rinunciare all'uso di geography in questo caso.


Dettagli del test che ho eseguito:

Ho creato due tabelle, una usando geography e un'altra usando decimal(9,6) per latitudine e longitudine:

CREATE TABLE [dbo].[GeographyTest]
(
    [RowId] [int] IDENTITY(1,1) NOT NULL,
    [Location] [geography] NOT NULL,
    CONSTRAINT [PK_GeographyTest] PRIMARY KEY CLUSTERED ( [RowId] ASC )
) 

CREATE TABLE [dbo].[LatLongTest]
(
    [RowId] [int] IDENTITY(1,1) NOT NULL,
    [Latitude] [decimal](9, 6) NULL,
    [Longitude] [decimal](9, 6) NULL,
    CONSTRAINT [PK_LatLongTest] PRIMARY KEY CLUSTERED ([RowId] ASC)
) 

e inserito una singola riga usando gli stessi valori di latitudine e longitudine in ogni tabella:

insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)

Infine, l'esecuzione del codice seguente mostra che, sulla mia macchina, la selezione di latitudine e longitudine è circa 5 volte più lenta quando si utilizza geography.

declare @lat float, @long float,
        @d datetime2, @repCount int, @trialCount int, 
        @geographyDuration int, @latlongDuration int,
        @trials int = 3, @reps int = 100000

create table #results 
(
    GeographyDuration int,
    LatLongDuration int
)

set @trialCount = 0

while @trialCount < @trials
begin

    set @repCount = 0
    set @d = sysdatetime()

    while @repCount < @reps
    begin
        select @lat = Location.Lat,  @long = Location.Long from GeographyTest where RowId = 1
        set @repCount = @repCount + 1
    end

    set @geographyDuration = datediff(ms, @d, sysdatetime())

    set @repCount = 0
    set @d = sysdatetime()

    while @repCount < @reps
    begin
        select @lat = Latitude,  @long = Longitude from LatLongTest where RowId = 1
        set @repCount = @repCount + 1
    end

    set @latlongDuration = datediff(ms, @d, sysdatetime())

    insert into #results values(@geographyDuration, @latlongDuration)

    set @trialCount = @trialCount + 1

end

select * 
from #results

select avg(GeographyDuration) as AvgGeographyDuration, avg(LatLongDuration) as AvgLatLongDuration
from #results

drop table #results

Risultati:

GeographyDuration LatLongDuration
----------------- ---------------
5146              1020
5143              1016
5169              1030

AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152                 1022

La cosa più sorprendente è che anche quando non sono state selezionate righe, ad esempio selezionando dove RowId = 2, Che non esiste, geography era ancora più lento:

GeographyDuration LatLongDuration
----------------- ---------------
1607              948
1610              946
1607              947

AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608                 947
104
Jeff Ogata

Se prevedi di eseguire qualsiasi calcolo spaziale, EF 5.0 consente espressioni LINQ come:

private Facility GetNearestFacilityToJobsite(DbGeography jobsite)
{   
    var q1 = from f in context.Facilities            
             let distance = f.Geocode.Distance(jobsite)
             where distance < 500 * 1609.344     
             orderby distance 
             select f;   
    return q1.FirstOrDefault();
}

Quindi c'è un'ottima ragione per usare la Geografia.

Spiegazione di spazio all'interno di Entity Framework .

Aggiornato con Creazione di database spaziali ad alte prestazioni

Come ho notato su Noel Abrahams Answer :

Nota sullo spazio, ogni coordinata viene memorizzata come un numero a virgola mobile a precisione doppia lungo 64 bit (8 byte) e il valore binario a 8 byte equivale all'incirca a 15 cifre di precisione decimale, quindi confrontando un decimale (9 , 6) che è di soli 5 byte, non è esattamente un confronto equo. Il decimale dovrebbe essere un minimo di decimale (15,12) (9 byte) per ogni LatLong (totale di 18 byte) per un confronto reale.

Quindi confrontando i tipi di archiviazione:

CREATE TABLE dbo.Geo
(    
geo geography
)
GO

CREATE TABLE dbo.LatLng
(    
    lat decimal(15, 12),   
    lng decimal(15, 12)
)
GO

INSERT dbo.Geo
SELECT geography::Point(12.3456789012345, 12.3456789012345, 4326) 
UNION ALL
SELECT geography::Point(87.6543210987654, 87.6543210987654, 4326) 

GO 10000

INSERT dbo.LatLng
SELECT  12.3456789012345, 12.3456789012345 
UNION
SELECT 87.6543210987654, 87.6543210987654

GO 10000

EXEC sp_spaceused 'dbo.Geo'

EXEC sp_spaceused 'dbo.LatLng'

Risultato:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   560 KB

Il tipo di dati geografico occupa il 30% di spazio in più.

Inoltre, il tipo di dati geografico non si limita alla sola memorizzazione di un punto, ma è anche possibile memorizzare LineString, CircularString, CompoundCurve, Polygon, CurvePolygon, GeometryCollection, MultiPoint, MultiLineString e MultiPolygon e altro . Qualsiasi tentativo di memorizzare anche il più semplice dei tipi di geografia (come Lat/Long) oltre un punto (ad esempio l'istanza LINESTRING (1 1, 2 2)) comporterà righe aggiuntive per ciascun punto, una colonna per il sequenziamento per l'ordine di ciascun punto e un'altra colonna per il raggruppamento di linee. SQL Server ha anche metodi per i tipi di dati Geografia che includono il calcolo Area, Limite, Lunghezza, Distanze e altro .

Sembra poco saggio memorizzare Latitudine e Longitudine come Decimali in SQL Server.

Aggiornamento 2

Se hai intenzione di fare calcoli come distanza, area, ecc., È difficile calcolarli correttamente sulla superficie della terra. Ogni tipo di geografia memorizzato in SQL Server è anche memorizzato con un ID di riferimento spaziale . Questi ID possono essere di sfere diverse (la terra è 4326). Ciò significa che i calcoli in SQL Server verranno effettivamente calcolati correttamente sulla superficie terrestre (anziché in linea d'aria che potrebbe essere attraverso la superficie terrestre).

enter image description here

65
Erik Philips

Un'altra cosa da considerare è lo spazio di archiviazione occupato da ciascun metodo. Il tipo di geografia è memorizzato come VARBINARY(MAX). Prova a eseguire questo script:

CREATE TABLE dbo.Geo
(
    geo geography

)

GO

CREATE TABLE dbo.LatLon
(
    lat decimal(9, 6)
,   lon decimal(9, 6)

)

GO

INSERT dbo.Geo
SELECT geography::Point(36.204824, 138.252924, 4326) UNION ALL
SELECT geography::Point(51.5220066, -0.0717512, 4326) 

GO 10000

INSERT dbo.LatLon
SELECT  36.204824, 138.252924 UNION
SELECT 51.5220066, -0.0717512

GO 10000

EXEC sp_spaceused 'dbo.Geo'
EXEC sp_spaceused 'dbo.LatLon'

Risultato:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   400 KB

Il tipo di dati geografico occupa quasi il doppio dello spazio.

6
Noel Abrahams