Vejamos um exemplo simples de usar a
STDistance
função no SQL Server 2008 (e posterior). Vou dizer ao SQL Server que estou em Londres e quero ver a distância de cada um dos meus escritórios. Aqui estão os resultados que eu quero que o SQL Server me dê:
Primeiro, precisaremos de alguns dados de amostra. Criaremos uma tabela contendo alguns locais de escritórios da Microsoft e armazenaremos seus valores de longitude e latitude em uma
geography
campo. CREATE TABLE [Offices] (
[Office_Id] [int] IDENTITY(1, 1) NOT NULL,
[Office_Name] [nvarchar](200) NOT NULL,
[Office_Location] [geography] NOT NULL,
[Update_By] nvarchar(30) NULL,
[Update_Time] [datetime]
) ON [PRIMARY]
GO
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Zurich', 'POINT(8.590847 47.408860 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft San Francisco', 'POINT(-122.403697 37.792062 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Paris', 'POINT(2.265509 48.833946)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Sydney', 'POINT(151.138378 -33.796572)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Dubai', 'POINT(55.286282 25.228850)', 'mike', GetDate())
Agora, supondo que estivéssemos em Londres. Veja como fazer uma
geography
valor fora dos valores de longitude e latitude de Londres:DECLARE
@latitude numeric(12, 7),
@longitude numeric(12, 7)
SET @latitude = 51.507351
SET @longitude = -0.127758
DECLARE @g geography = 'POINT(' + cast(@longitude as nvarchar) + ' ' + cast(@latitude as nvarchar) + ')';
E, finalmente, vamos ver a que distância cada um de nossos escritórios está.
SELECT [Office_Name],
cast([Office_Location].STDistance(@g) / 1609.344 as numeric(10, 1)) as 'Distance (in miles)'
FROM [Offices]
ORDER BY 2 ASC
E isso nos dá os resultados que esperávamos.
Obviamente, você pode inserir um
TOP(1)
se você só queria ver o mais próximo escritório. Legal, hein?
Há apenas um problema. Quando você tem muita
geography
pontos para comparar, o desempenho não é brilhante, mesmo se você adicionar um ÍNDICE ESPACIAL nesse campo do banco de dados. Testei um ponto em uma tabela de 330.000
geography
pontos. Usando o código mostrado aqui, ele encontrou o ponto mais próximo em cerca de 8 segundos . Quando modifiquei minha tabela para armazenar os valores de longitude e latitude e usei o
[dbo].[fnCalcDistanceMiles]
função deste artigo do StackOverflow, ele encontrou o ponto mais próximo em cerca de 3 segundos . No entanto...
Todos os exemplos de "distância entre dois pontos" que encontrei na Internet usaram o SQL Server
STDistance
função, ou fórmulas matemáticas que envolvem as funções (com uso intensivo de CPU), cos, sin e tan. Uma solução mais rápida era viajar no tempo para o ensino médio e lembrar como Pitágoras calculou a distância entre dois pontos.
Supondo que quiséssemos saber a distância entre Londres e Paris.
E aqui está minha função do SQL Server:
CREATE FUNCTION [dbo].[uf_CalculateDistance] (@Lat1 decimal(8,4), @Long1 decimal(8,4), @Lat2 decimal(8,4), @Long2 decimal(8,4))
RETURNS decimal (8,4) AS
BEGIN
DECLARE @d decimal(28,10)
SET @d = sqrt(square(@[email protected]) + square(@[email protected]))
RETURN @d
END
Agora, lembre-se que esta função não retorna um valor em milhas, quilômetros, etc... ela está apenas comparando os valores de longitude e latitude. E Pitágoras deve ser usado em 2D, e não comparando pontos em um planeta redondo!
No entanto, em meus testes, ele encontrou o ponto mais próximo em 1 segundo , e produziu os mesmos resultados usando o
STDistance
do SQL Server função. Portanto, sinta-se à vontade para usar esta função para comparar distâncias relativas , mas não use esta função se você precisar da distância real em si.
Espero que tudo isso ajude.