Przeprojektowuję bazę danych klientów, a jedną z nowych informacji, które chciałbym przechowywać wraz ze standardowymi polami adresowymi (Ulica, Miasto, itp.) Jest lokalizacja geograficzna adresu. Jedynym przypadkiem użycia, o którym myślę, jest umożliwienie użytkownikom mapowania współrzędnych na mapach Google, gdy nie można znaleźć adresu w inny sposób, co często ma miejsce, gdy obszar jest nowo zagospodarowany lub znajduje się w odległej / wiejskiej lokalizacji.
Moją pierwszą skłonnością było przechowywanie szerokości i długości geograficznej jako wartości dziesiętnych, ale potem przypomniałem sobie, że SQL Server 2008 R2 ma geography
typ danych. Nie mam absolutnie żadnego doświadczenia w używaniu geography
, a z moich początkowych badań wynika, że jest to przesada w moim scenariuszu.
Na przykład, aby pracować z szerokością i długością geograficzną zapisaną jako decimal(7,4)
, mogę to zrobić:
insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest
ale z geography
zrobiłbym to:
insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest
Chociaż nie jest to o wiele bardziej skomplikowane, po co dodawać złożoność, jeśli nie muszę?
Zanim porzucę pomysł używania geography
, czy jest coś, co powinienem rozważyć? Czy wyszukiwanie lokalizacji przy użyciu indeksu przestrzennego byłoby szybsze niż indeksowanie pól szerokości i długości geograficznej? Czy są zalety używania geography
, o których nie wiem? A może z drugiej strony, czy są zastrzeżenia, o których powinienem wiedzieć, a które zniechęciłyby mnie do używania geography
?
Aktualizacja
@Erik Philips wspomniał o możliwości wyszukiwania w pobliżu geography
, co jest bardzo fajne.
Z drugiej strony szybki test pokazuje, że proste select
uzyskanie szerokości i długości geograficznej jest znacznie wolniejsze podczas używania geography
(szczegóły poniżej). , a komentarz dotyczący zaakceptowanej odpowiedzi na inne pytanie SO na temat geography
mnie niepokoi:
@SaphuA Nie ma za co. Na marginesie należy BARDZO ostrożnie używać indeksu przestrzennego w kolumnie typu danych GEOGRAPHY dopuszczającej wartość null. Występują poważne problemy z wydajnością, więc spraw, aby kolumna GEOGRAPHY nie dopuszczała wartości null, nawet jeśli musisz przebudować swój schemat. - Tomas 18 czerwca o 11:18
Podsumowując, biorąc pod uwagę prawdopodobieństwo wykonania wyszukiwania zbliżeniowego w porównaniu z kompromisem w wydajności i złożoności, zdecydowałem się zrezygnować z użycia geography
w tym przypadku.
Szczegóły testu, który przeprowadziłem:
Utworzyłem dwie tabele, jedną używającą, geography
a drugą używającą decimal(9,6)
szerokości i długości geograficznej:
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)
)
i wstawił pojedynczy wiersz z tymi samymi wartościami szerokości i długości geograficznej do każdej tabeli:
insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)
Wreszcie uruchomienie poniższego kodu pokazuje, że na moim komputerze wybranie szerokości i długości geograficznej jest około 5 razy wolniejsze podczas korzystania z 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
Wyniki:
GeographyDuration LatLongDuration
----------------- ---------------
5146 1020
5143 1016
5169 1030
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152 1022
Bardziej zaskakujące było to, że nawet jeśli nie wybrano żadnych wierszy, na przykład wybranie miejsca RowId = 2
, w którym nie ma, geography
było nadal wolniejsze:
GeographyDuration LatLongDuration
----------------- ---------------
1607 948
1610 946
1607 947
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608 947
źródło
Odpowiedzi:
Jeśli planujesz wykonywać jakiekolwiek obliczenia przestrzenne, EF 5.0 zezwala na wyrażenia LINQ, takie jak:
Jest też bardzo dobry powód, aby skorzystać z Geografii.
Wyjaśnienie przestrzenności w ramach Entity Framework .
Zaktualizowano o tworzenie wysokowydajnych przestrzennych baz danych
Jak zauważyłem w odpowiedzi Noela Abrahamsa :
Porównanie typów pamięci:
Wynik:
Typ danych geograficznych zajmuje o 30% więcej miejsca.
Ponadto typ danych geograficznych nie ogranicza się tylko do przechowywania punktu, można również przechowywać ciąg liniowy, ciąg kołowy, krzywa złożona, wielokąt, krzywaPolygon, zbiór geometryczny, wielopunktowy, ciąg wieloliniowy i wielobok i nie tylko . Każda próba zapisania nawet najprostszych typów geograficznych (takich jak szerokość / długość) poza punktem (na przykład instancja LINESTRING (1 1, 2 2)) spowoduje naliczenie dodatkowych wierszy dla każdego punktu, kolumny do sekwencjonowania dla kolejności każdego punktu i kolejna kolumna do grupowania linii. SQL Server ma również metody dla typów danych geograficznych, które obejmują obliczanie powierzchni , granicy, długości, odległości i innych .
Przechowywanie szerokości i długości geograficznej jako liczby dziesiętnej na serwerze Sql wydaje się nierozsądne.
Zaktualizuj 2
Jeśli planujesz wykonać jakiekolwiek obliczenia, takie jak odległość, powierzchnia itp., Prawidłowe obliczenie ich na powierzchni ziemi jest trudne. Każdy typ geograficzny przechowywany w programie SQL Server jest również przechowywany z identyfikatorem odniesienia przestrzennego . Te identyfikatory mogą pochodzić z różnych sfer (ziemia to 4326). Oznacza to, że obliczenia w programie SQL Server będą faktycznie obliczane poprawnie na powierzchni ziemi (zamiast w locie w locie, które mogłyby przebiegać przez powierzchnię ziemi).
źródło
geography
a ty podałeś kilka dobrych. Ostatecznie zdecydowałem się po prostu użyćdecimal
pól w tym przypadku (zobacz moją rozwlekłą aktualizację), ale dobrze jest wiedzieć, że mogę użyć,geography
jeśli kiedykolwiek będę musiał zrobić coś bardziej wyszukanego niż po prostu odwzorować współrzędne.Inną rzeczą do rozważenia jest miejsce zajmowane przez każdą metodę. Typ geograficzny jest przechowywany jako
VARBINARY(MAX)
. Spróbuj uruchomić ten skrypt:Wynik:
Typ danych geograficznych zajmuje prawie dwa razy więcej miejsca.
źródło
źródło