Si todo lo que quiere hacer con SqlGeography es rastrear puntos y aprovechar los índices espaciales de SQL Server 2008, puede, como han señalado otros, ocultar su columna de datos espaciales de Linq a SQL y usar UDF o procedimientos almacenados. Suponga que tiene una tabla AddressFields que incluye campos de Latitud y Longitud. Agregue esa tabla a su archivo DBML y escriba cualquier código que desee que establezca los campos de Latitud y Longitud. Luego, el código SQL a continuación agregará un campo Geo geogarphy a esa tabla y creará un activador en la base de datos que establece automáticamente el campo Geo en función de los campos Latitud y Longitud. Mientras tanto, el siguiente código también crea otros UDF útiles y procedimientos almacenados:DistanceBetween2 (ya tenía un DistanceBetween) devuelve la distancia entre la dirección representada en un AddressField y un par específico de latitud/longitud; DistanceWithin devuelve varios campos de todos los AddressFields dentro de una distancia de milla específica; UDFDistanceWithin hace lo mismo que una función definida por el usuario (útil si desea incrustar esto en una consulta más grande); y UDFNearestNeighbors devuelve campos de AddressField correspondientes al número especificado de vecinos más cercanos a un punto en particular. (Un motivo para usar UDFNeighborsNeighbors es que SQL Server 2008 no optimizará el uso de un índice espacial si simplemente llama a order llamando a DistanceBetween2).
Deberá personalizar esto cambiando AddressFields a su tabla y personalizando los campos de esa tabla que desea devolver (consulte el código alrededor de las referencias a AddressFieldID). Luego puede ejecutar esto en su base de datos y copiar los procedimientos almacenados y UDF resultantes en su DBML, y luego puede usarlos en consultas. En general, esto le permite aprovechar un índice espacial de puntos con bastante facilidad.
-----------------------------------------------------------------------------------------
--[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] CREAR ÍNDICE
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] CREAR PROCEDIMIENTO USP_SET_GEO_VALUE PARA 1 LATITUD 2 LONGITUD
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] CREAR DISPARADOR EN CAMBIO DE VALOR LAT/LARGO/INSERTAR ---> ESTABLECER GEOCÓDIGO
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] CREAR PROC USP_SET_GEO_VALUE_INITIAL_LOAD ----> EJECUCIÓN ÚNICA
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, que devuelve la distancia entre dos puntos especificados
--por pares de coordenadas de latitud/longitud. --ALTER PROC Distancia entre 2
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
IR--PRUEBA
DistanciaEntre2 12159,40.75889600,-73.99228900
--[8] CREAR PROCEDIMIENTO USPDistanciaDentro
-- DEVUELVE LA LISTA DE DIRECCIONES DESDE la tabla AddressFields
SI EXISTE (SELECCIONE el nombre DE sysobjects DONDE nombre ='USPDistanciaDentro' Y tipo ='P') ABANDONAR EL PROCEDIMIENTO USPDistanciaDentro
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
IR
--PRUEBA
--Dentro de 3 millasUSPDistanciaDentro de 38.90606200,-76.92943500,3GO--Dentro de 5 millasUSPDistanciaDentro de 38.90606200,-76.92943500,5GO--Dentro de 10 millasUSPDistanciaDentro de 38.90606200,-76.92943500,10
--[9] CREAR FUNCIÓN FNDistanceWithin
-- DEVUELVE LA LISTA DE DIRECCIONES DESDE la tabla AddressFields
SI EXISTE (SELECCIONE el nombre DE sysobjects DONDE nombre ='UDFDistanceWithin' Y tipo ='TF') FUNCIÓN DE CAÍDA 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
IR
--PRUEBA
--dentro de 3 millasseleccione * de UDFDistanceWithin(38.90606200,-76.92943500,3)GO--dentro de 5 millasseleccione * de UDFDistanceWithin(38.90606200,-76.92943500,5)GO--dentro de 10 millasseleccione * de UDFDistanceWithin(38.906062090,1036.
--[9] CREAR FUNCIÓN UDFVecinos más cercanos
-- DEVUELVE LA LISTA DE DIRECCIONES DESDE la tabla AddressFields
SI EXISTE (SELECCIONE el nombre DE sysobjects DONDE nombre ='UDFNearestNeighbors' Y tipo ='TF') DROP FUNCIÓN UDFNeighborsNeighbors
GO
SI EXISTE (SELECCIONE el nombre DE sysobjects DONDE name ='numbers' AND xtype ='u') DROP TABLE number
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
IR
--PRUEBA
--50 vecinos seleccionen * de UDFNearestNeighbors(38.90606200,-76.92943500,50)GO--200 vecinosselecten * de UDFNearestNeighbors(38.90606200,-76.92943500,200)GO