Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Calcul de distance avec une énorme base de données SQL Server

Vous pourriez faire pire que de regarder la GEOGRAPHY type de données, par exemple :

CREATE TABLE Places
(
    SeqID       INT IDENTITY(1,1),
    Place       NVARCHAR(20),
    Location    GEOGRAPHY
)
GO
INSERT INTO Places (Place, Location) VALUES ('Coventry', geography::Point(52.4167, -1.55, 4326))
INSERT INTO Places (Place, Location) VALUES ('Sheffield', geography::Point(53.3667, -1.5, 4326))
INSERT INTO Places (Place, Location) VALUES ('Penzance', geography::Point(50.1214, -5.5347, 4326))
INSERT INTO Places (Place, Location) VALUES ('Brentwood', geography::Point(52.6208, 0.3033, 4326))
INSERT INTO Places (Place, Location) VALUES ('Inverness', geography::Point(57.4760, -4.2254, 4326))
GO
SELECT p1.Place, p2.place, p1.location.STDistance(p2.location) / 1000 AS DistanceInKilometres
    FROM Places p1
    CROSS JOIN Places p2
GO  
SELECT p1.Place, p2.place, p1.location.STDistance(p2.location) / 1000 AS DistanceInKilometres
    FROM Places p1
        INNER JOIN Places p2 ON p1.SeqID > p2.SeqID
GO  

geography::Point prend la latitude et la longitude ainsi qu'un SRID (Special Reference ID number). Dans ce cas, le SRID est 4326 qui est la latitude et la longitude standard. Comme vous avez déjà la latitude et la longitude, vous pouvez simplement ALTER TABLE pour ajouter la colonne géographie puis UPDATE pour le remplir.

J'ai montré deux façons d'extraire les données de la table, mais vous ne pouvez pas créer de vue indexée avec cela (les vues indexées ne peuvent pas avoir d'auto-jointures). Vous pouvez cependant créer une table secondaire qui est en fait un cache, qui est remplie en fonction de ce qui précède. Vous n'avez ensuite qu'à vous soucier de sa maintenance (cela peut être fait via des déclencheurs ou un autre processus).

Notez que la jointure croisée vous donnera 250 000 000 000 lignes, mais la recherche est simple car vous n'avez qu'à regarder l'une des colonnes de lieux (c'est-à-dire SELECT * FROM table WHERE Place1 = 'Sheffield' AND distance < 100 , la seconde vous donnera beaucoup moins de lignes, mais la requête doit alors prendre en compte à la fois les colonnes Place1 et Place2).