Als u met SqlGeography alleen maar punten wilt volgen en wilt profiteren van de ruimtelijke indices van SQL Server 2008, kunt u, zoals anderen al hebben opgemerkt, uw kolom met ruimtelijke gegevens verbergen voor Linq naar SQL en UDF's of opgeslagen procedures gebruiken. Stel dat u een tabel AddressFields hebt die de velden Latitude en Longitude bevat. Voeg die tabel toe aan uw DBML-bestand en schrijf elke gewenste code die de velden Latitude en Longitude instelt. Vervolgens voegt de onderstaande SQL-code een Geo-geogarphy-veld toe aan die tabel en creëert een trigger in de database die automatisch het Geo-veld instelt op basis van de velden Latitude en Longitude. Ondertussen creëert de onderstaande code ook andere nuttige UDF's en opgeslagen procedures:DistanceBetween2 (ik had al een DistanceBetween) retourneert de afstand tussen het adres dat wordt weergegeven in een AddressField en een opgegeven breedtegraad/lengtegraad-paar; DistanceWithin retourneert verschillende velden van alle AddressFields binnen een opgegeven mijlafstand; UDFDistanceWithin doet hetzelfde als een door de gebruiker gedefinieerde functie (handig als u deze in een grotere query wilt insluiten); en UDFNearestNeighbors retourneert velden uit AddressField die overeenkomen met het opgegeven aantal buren dat het dichtst bij een bepaald punt ligt. (Een reden voor het gebruik van UDFNearestNeighbours is dat SQL Server 2008 het gebruik van een ruimtelijke index niet zal optimaliseren als u gewoon de volgorde oproept door DistanceBetween2 aan te roepen.)
U moet dit aanpassen door AddressFields in uw tabel te wijzigen en de velden uit die tabel aan te passen die u wilt retourneren (kijk in de code rond verwijzingen naar AddressFieldID). U kunt dit vervolgens op uw database uitvoeren en de resulterende opgeslagen procedures en UDF's naar uw DBML kopiëren, en deze vervolgens in query's gebruiken. Over het algemeen kunt u hierdoor vrij gemakkelijk profiteren van een ruimtelijke index van punten.
-----------------------------------------------------------------------------------------
--[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] MAAK 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] PROCEDURE MAKEN 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] MAAK TRIGGER OP LAT/LONG VALUE CHANGE/INSERT ---> STEL GEOCODE IN
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] MAAK PROC USP_SET_GEO_VALUE_INITIAL_LOAD ----> SLECHTS EENMALIG UITVOEREN
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] BESTAANDE PROC DistanceBetween, die de afstand tussen twee gespecificeerde punten retourneert
--door breedtegraad/lengtegraad coördinaatparen. --ALTER PROC DistanceBetween2
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
GO--TEST
DistanceBetween2 12159.40.75889600,-73.99228900
--[8] PROCEDURE MAKEN USPDistanceWithin
-- RETURN LIJST MET ADRESSEN VANUIT AddressFields-tabel
IF BESTAAT (SELECT naam FROM sysobjects WHERE naam ='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
GO
--TEST
--binnen 3 mijlUSPDistanceBinnen 38.90606200,-76.92943500,3GO--binnen 5 mijlUSPDistanceBinnen 38.90606200,-76.92943500,5GO--binnen 10 mijlUSPDistanceBinnen 38.90606200,-76.92943500,10
--[9] MAAK FUNCTIE FNDistanceWithin
-- RETURN LIJST MET ADRESSEN VANUIT AddressFields-tabel
IF EXISTS (SELECT name FROM sysobjects WHERE name ='UDFDistanceWithin' AND type ='TF')DROP-FUNCTIE 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
GO
--TEST
--binnen 3 mijlselecteer * van UDFDistanceWithin(38.90606200,-76.92943500,3)GO--binnen 5 mijlselecteer * van UDFDistanceWithin( 38.90606200,-76.92943500,5)GO-binnen 10 mijlselecteer * van UDFDistanceWithin( 38.90606200,-76.92943500,10
--[9] FUNCTIE MAKEN UDF Dichtstbijzijnde Buren
-- RETURN LIJST MET ADRESSEN VANUIT AddressFields-tabel
IF EXISTS (SELECT name FROM sysobjects WHERE name ='UDFNearestNeighbors' AND type ='TF')DROP-FUNCTIE UDFNearestNeighbors
GO
INDIEN BESTAAT (SELECT naam FROM sysobjects WHERE naam ='nummers' AND xtype ='u')DROP TABLE-nummers
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
GO
--TEST
--50 burenselecteer * van UDFNearestNeighbours(38.90606200,-76.92943500,50)GO--200 burenselecteer * van UDFNearestNeighbours( 38.90606200,-76.92943500,200)GO