web-dev-qa-db-fra.com

Pourquoi utiliser le type de données géographiques SQL Server 2008?

Je suis en train de remodeler une base de données de clients et l'une des nouvelles informations que j'aimerais stocker, ainsi que les champs d'adresse standard (rue, ville, etc.) est l'emplacement géographique de l'adresse. Le seul cas d'utilisation que je me propose est de permettre aux utilisateurs de cartographier les coordonnées sur des cartes Google lorsque l'adresse est introuvable, ce qui se produit souvent lorsque la zone est nouvellement développée ou située dans une zone reculée ou rurale.

Ma première tendance était de stocker la latitude et la longitude sous forme décimale, mais je me suis souvenu que SQL Server 2008 R2 avait un type de données geography. Je n'ai absolument aucune expérience d'utilisation de geography et, d'après mes recherches initiales, le scénario semble exagéré.

Par exemple, pour travailler avec la latitude et la longitude stockées sous la forme decimal(7,4), je peux le faire:

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

mais avec geography, je ferais ceci:

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

Bien que ce ne soit pas aussi beaucoup plus compliqué, pourquoi ajouter de la complexité si ce n'est pas nécessaire?

Avant d’abandonner l’idée d’utiliser geography, puis-je envisager quelque chose? Serait-il plus rapide de rechercher un emplacement à l'aide d'un index spatial plutôt que d'indexer les champs Latitude et Longitude? Y a-t-il des avantages à utiliser geography dont je ne suis pas au courant? Ou bien, y a-t-il des mises en garde que je devrais connaître qui me décourageraient d'utiliser geography?


Mise à jour

@Erik Philips a évoqué la possibilité d'effectuer des recherches de proximité avec geography, ce qui est très pratique.

D'autre part, un test rapide montre qu'un simple select pour obtenir la latitude et la longitude est nettement plus lent lorsque vous utilisez geography (détails ci-dessous). , et un commentaire sur le réponse acceptée à un autre SO question sur geography m'a méprisé:

@SaphuA de rien. En tant que note, soyez TRÈS prudent d'utiliser un index spatial sur une colonne de type de données GEOGRAPHY à valeur NULL. Il y a de sérieux problèmes de performances. Par conséquent, assurez-vous que la colonne GEOGRAPHY soit non nullable même si vous devez remodeler votre schéma. - Tomas 18 juin à 11h18

Globalement, en pesant la probabilité de faire des recherches de proximité par rapport aux compromis entre performances et complexité, j'ai décidé de renoncer à l'utilisation de geography dans ce cas.


Détails du test que j'ai effectué:

J'ai créé deux tables, l'une utilisant geography et l'autre utilisant decimal(9,6) pour la latitude et la longitude:

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)
) 

et inséré une seule ligne en utilisant les mêmes valeurs de latitude et de longitude dans chaque tableau:

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

Enfin, l’exécution du code suivant montre que, sur ma machine, la sélection de la latitude et de la longitude est environ 5 fois plus lente lorsqu’on utilise 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

Résultats:

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

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

Ce qui était plus surprenant, c'est que même lorsqu'aucune ligne n'est sélectionnée, par exemple lorsque RowId = 2, Qui n'existe pas, geography était encore plus lent:

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

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

Si vous envisagez d'effectuer un calcul spatial, EF 5.0 autorise les expressions LINQ telles que:

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();
}

Alors il y a une très bonne raison d'utiliser la géographie.

Explication de l'espace dans Entity Framework .

Mis à jour avec Création de bases de données spatiales hautes performances

Comme je l'ai noté sur Noel Abrahams Answer :

Une note sur l’espace, chaque coordonnée est stockée sous la forme d’un nombre à virgule flottante à double précision de 64 bits (8 octets) de long, et une valeur binaire de 8 octets équivaut à environ 15 chiffres de précision décimale. , 6) qui n’est que de 5 octets, n’est pas une comparaison juste. La virgule décimale doit être au minimum décimale (15,12) (9 octets) pour chaque LatLong (total de 18 octets) pour une comparaison réelle.

Donc, en comparant les types de stockage:

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'

Résultat:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   560 KB

Le type de données geography occupe 30% plus d'espace.

De plus, le type de données geography ne se limite pas à stocker un point, vous pouvez également stocker LineString, CircularString, CompoundCurve, Polygon, CurvePolygon, GeometryCollection, MultiPoint, MultiLineString et MultiPolygon, etc. . Toute tentative de stocker même les types de géographie les plus simples (Lat/Long) au-delà d'un point (par exemple, instance LINESTRING (1 1, 2 2)) entraînera des lignes supplémentaires pour chaque point, une colonne permettant de séquencer l'ordre de chaque point. et une autre colonne pour regrouper les lignes. SQL Server propose également des méthodes pour les types de données Geography, notamment le calcul Surface, limite, longueur, distances, etc. .

Il semble peu judicieux de stocker Latitude et Longitude sous forme décimale dans Sql Server.

Mise à jour 2

Si vous envisagez de faire des calculs tels que la distance, la surface, etc., il est difficile de les calculer correctement à la surface de la Terre. Chaque type de géographie stocké dans SQL Server est également stocké avec un ID de référence spatiale . Ces identifiants peuvent être de différentes sphères (la Terre est 4326). Cela signifie que les calculs effectués dans SQL Server seront calculés correctement sur la surface de la Terre (au lieu de à vol d'oisea qui pourrait être à la surface de la Terre).

enter image description here

65
Erik Philips

Une autre chose à considérer est l’espace de stockage occupé par chaque méthode. Le type de géographie est enregistré en tant que VARBINARY(MAX). Essayez d'exécuter ce 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'

Résultat:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   400 KB

Le type de données geography occupe presque deux fois plus d'espace.

6
Noel Abrahams