Si tout ce que vous voulez faire avec SqlGeography est de suivre des points et de tirer parti des index spatiaux de SQL Server 2008, vous pouvez, comme d'autres l'ont noté, masquer votre colonne de données spatiales de Linq vers SQL et utiliser des UDF ou des procédures stockées. Supposons que vous disposiez d'une table AddressFields comprenant des champs Latitude et Longitude. Ajoutez cette table à votre fichier DBML et écrivez le code de votre choix qui définit les champs Latitude et Longitude. Ensuite, le code SQL ci-dessous ajoutera un champ Geo geogarphy à cette table et créera un déclencheur dans la base de données qui définit automatiquement le champ Geo en fonction des champs Latitude et Longitude. Pendant ce temps, le code ci-dessous crée également d'autres UDF et procédures stockées utiles :DistanceBetween2 (j'avais déjà un DistanceBetween) renvoie la distance entre l'adresse représentée dans un champ d'adresse et une paire latitude/longitude spécifiée ; DistanceWithin renvoie divers champs de tous les AddressFields à une distance en milles spécifiée ; UDFDistanceWithin fait la même chose qu'une fonction définie par l'utilisateur (utile si vous souhaitez l'intégrer dans une requête plus large) ; et UDFNearestNeighbors renvoie les champs de AddressField correspondant au nombre spécifié de voisins les plus proches d'un point particulier. (L'une des raisons d'utiliser UDFNearestNeighbors est que SQL Server 2008 n'optimisera pas son utilisation d'un index spatial si vous appelez simplement order en appelant DistanceBetween2.)
Vous devrez personnaliser cela en modifiant AddressFields dans votre table et en personnalisant les champs de cette table que vous souhaitez renvoyer (regardez dans le code autour des références à AddressFieldID). Vous pouvez ensuite l'exécuter sur votre base de données et copier les procédures stockées et les UDF résultantes sur votre DBML, puis vous pouvez les utiliser dans des requêtes. Globalement, cela permet de profiter assez facilement d'un index spatial de points.
-----------------------------------------------------------------------------------------
--[1]
--INITIAL AUDIT
select * from dbo.AddressFields
GO
--ADD COLUMN GEO
IF EXISTS (SELECT name FROM sysindexes WHERE name = 'SIndx_AddressFields_geo')
DROP INDEX SIndx_AddressFields_geo ON AddressFields
GO
IF EXISTS (SELECT b.name FROM sysobjects a, syscolumns b
WHERE a.id = b.id and a.name = 'AddressFields' and b.name ='Geo' and a.type ='U' )
ALTER TABLE AddressFields DROP COLUMN Geo
GO
alter table AddressFields add Geo geography
--[2]
--SET GEO VALUE
GO
UPDATE AddressFields
SET Geo = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' +
CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
--[3] CRÉER UN INDEX
IF EXISTS (SELECT name FROM sysindexes WHERE name = 'SIndx_AddressFields_geo')
DROP INDEX SIndx_AddressFields_geo ON AddressFields
GO
CREATE SPATIAL INDEX SIndx_AddressFields_geo
ON AddressFields(geo)
--UPDATE STATS
UPDATE STATISTICS AddressFields
--AUDIT
GO
select * from dbo.AddressFields
--[4] CRÉER PROCÉDURE USP_SET_GEO_VALUE PARA 1 LATITUDE 2 LONGITUDE
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'USPSetGEOValue' AND type = 'P')
DROP PROC USPSetGEOValue
GO
GO
CREATE PROC USPSetGEOValue @latitude decimal(18,8), @longitude decimal(18,8)
AS
UPDATE AddressFields
SET Geo = geography::STPointFromText('POINT(' + CAST(@longitude AS VARCHAR(20)) + ' ' +
CAST(@latitude AS VARCHAR(20)) + ')', 4326)
WHERE [Longitude] [email protected] and [Latitude] = @latitude
GO
--TEST
EXEC USPSetGEOValue 38.87350500,-76.97627500
GO
--[5] CRÉER UN DÉCLENCHEUR SUR LE CHANGEMENT/INSÉRER DE VALEUR LAT/LONG ---> DÉFINIR LE GÉOCODE
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'TRGSetGEOCode' AND type = 'TR')
DROP TRIGGER TRGSetGEOCode
GO
CREATE TRIGGER TRGSetGEOCode
ON AddressFields
AFTER INSERT,UPDATE
AS
DECLARE @latitude decimal(18,8), @longitude decimal(18,8)
IF ( UPDATE (Latitude) OR UPDATE (Longitude) )
BEGIN
SELECT @latitude = latitude ,@longitude = longitude from inserted
UPDATE AddressFields
SET Geo = geography::STPointFromText('POINT(' + CAST(@longitude AS VARCHAR(20)) + ' ' +
CAST(@latitude AS VARCHAR(20)) + ')', 4326)
WHERE [Longitude] [email protected] and [Latitude] = @latitude
END
ELSE
BEGIN
SELECT @latitude = latitude ,@longitude = longitude from inserted
UPDATE AddressFields
SET Geo = geography::STPointFromText('POINT(' + CAST(@longitude AS VARCHAR(20)) + ' ' +
CAST(@latitude AS VARCHAR(20)) + ')', 4326)
WHERE [Longitude] [email protected] and [Latitude] = @latitude
END
GO
--[6] CREATE PROC USP_SET_GEO_VALUE_INITIAL_LOAD ----> EXÉCUTION UNIQUEMENT UNE SEULE FOIS
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'USPSetAllGeo' AND type = 'P')
DROP PROC USPSetAllGeo
GO
CREATE PROC USPSetAllGeo
AS
UPDATE AddressFields
SET Geo = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' +
CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
GO
--[7] EXISTING PROC DistanceBetween, qui renvoie la distance entre deux points spécifiés
--par paires de coordonnées latitude/longitude. --ALTER PROC DistanceEntre2
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'DistanceBetween2' AND type = 'FN')
DROP FUNCTION DistanceBetween2
GO
CREATE FUNCTION [dbo].[DistanceBetween2]
(@AddressFieldID as int, @Lat1 as real,@Long1 as real)
RETURNS real
AS
BEGIN
DECLARE @KMperNM float = 1.0/1.852;
DECLARE @nwi geography =(select geo from addressfields where AddressFieldID = @AddressFieldID)
DECLARE @edi geography = geography::STPointFromText('POINT(' + CAST(@Long1 AS VARCHAR(20)) + ' ' +
CAST(@Lat1 AS VARCHAR(20)) + ')', 4326)
DECLARE @dDistance as real = (SELECT (@nwi.STDistance(@edi)/1000.0) * @KMperNM)
return (@dDistance);
END
ALLER - TESTER
DistanceEntre2 12159,40.75889600,-73.99228900
--[8] CRÉER PROCÉDURE USPDistanceWithin
-- RENVOIE LA LISTE DES ADRESSES DE LA table AddressFields
IF EXISTS (SELECT name FROM sysobjects WHERE name ='USPDistanceWithin' AND type ='P')DROP PROCEDURE USPDistanceWithin
GO
CREATE PROCEDURE [dbo].USPDistanceWithin
(@lat as real,@long as real, @distance as float)
AS
BEGIN
DECLARE @edi geography = geography::STPointFromText('POINT(' + CAST(@Long AS VARCHAR(20)) + ' ' +
CAST(@Lat AS VARCHAR(20)) + ')', 4326)
SET @distance = @distance * 1609.344 -- convert distance into meter
select
AddressFieldID
,FieldID
,AddressString
,Latitude
,Longitude
,LastGeocode
,Status
--,Geo
from
AddressFields a WITH(INDEX(SIndx_AddressFields_geo))
where
a.geo.STDistance(@edi) < = @Distance
END
ALLER
--TEST
--dans un rayon de 3 milesUSPDistancedans un rayon de 38,90606200,-76,92943500,3GO--dans un rayon de 5 milesUSPDistancedans un rayon de 38,90606200,-76,92943500,5GO--dans un rayon de 10 milesUSPDistancedans un rayon de 38,90606200,-76,92943500,10
--[9] CRÉER LA FONCTION FNDistanceDans
-- RENVOIE LA LISTE DES ADRESSES DE LA table AddressFields
IF EXISTS (SELECT name FROM sysobjects WHERE name ='UDFDistanceWithin' AND type ='TF')DROP FUNCTION UDFDistanceWithin
GO
CREATE FUNCTION UDFDistanceWithin
(@lat as real,@long as real, @distance as real)
RETURNS @AddressIdsToReturn TABLE
(
AddressFieldID INT
,FieldID INT
)
AS
BEGIN
DECLARE @edi geography = geography::STPointFromText('POINT(' + CAST(@Long AS VARCHAR(20)) + ' ' +
CAST(@Lat AS VARCHAR(20)) + ')', 4326)
SET @distance = @distance * 1609.344 -- convert distance into meter
INSERT INTO @AddressIdsToReturn
select
AddressFieldID
,FieldID
from
AddressFields a WITH(INDEX(SIndx_AddressFields_geo))
where
a.geo.STDistance(@edi) < = @Distance
RETURN
END
ALLER
--TEST
--dans un rayon de 3 milesselect * from UDFDistanceWithin(38.90606200,-76.92943500,3)GO--dans un rayon de 5 milesselect * from UDFDistanceWithin( 38.90606200,-76.92943500,5)GO--dans un rayon de 10 milesselect * from UDFDistanceWithin( 38.90606200,-736.92)
--[9] CRÉER LA FONCTION UDFNearestNeighbors
-- RENVOIE LA LISTE DES ADRESSES DE LA table AddressFields
IF EXISTS (SELECT name FROM sysobjects WHERE name ='UDFNearestNeighbors' AND type ='TF')DROP FUNCTION UDFNearestNeighbors
GO
IF EXISTS (SELECT name FROM sysobjects WHERE name ='numbers' AND xtype ='u')DROP TABLE numbers
GO
-- First, create a Numbers table that we will use below.
SELECT TOP 100000 IDENTITY(int,1,1) AS n INTO numbers FROM MASTER..spt_values a, MASTER..spt_values b CREATE UNIQUE CLUSTERED INDEX idx_1 ON numbers(n)
GO
CREATE FUNCTION UDFNearestNeighbors
(@lat as real,@long as real, @neighbors as int)
RETURNS @AddressIdsToReturn TABLE
(
AddressFieldID INT
,FieldID INT
)
AS
BEGIN
DECLARE @edi geography = geography::STPointFromText('POINT(' + CAST(@Long AS VARCHAR(20)) + ' ' +
CAST(@Lat AS VARCHAR(20)) + ')', 4326)
DECLARE @start FLOAT = 1000;
WITH NearestPoints AS
(
SELECT TOP(@neighbors) WITH TIES *, AddressFields.geo.STDistance(@edi) AS dist
FROM Numbers JOIN AddressFields WITH(INDEX(SIndx_AddressFields_geo))
ON AddressFields.geo.STDistance(@edi) < @start*POWER(2,Numbers.n)
ORDER BY n
)
INSERT INTO @AddressIdsToReturn
SELECT TOP(@neighbors)
AddressFieldID
,FieldID
FROM NearestPoints
ORDER BY n DESC, dist
RETURN
END
ALLER
--TEST
--50 voisinssélectionnez * parmi UDFNeighbors(38.90606200,-76.92943500,50)GO--200 voisinssélectionnez * parmi UDFNearestNeighbors( 38.90606200,-76.92943500,200)GO