Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Obter a longitude e latitude mais próxima da tabela do banco de dados MSSQL?


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.