Nếu tất cả những gì bạn muốn làm với SqlGeography là các điểm theo dõi và tận dụng các chỉ số không gian của SQL Server 2008, như những người khác đã lưu ý, bạn có thể ẩn cột dữ liệu không gian của mình khỏi Linq to SQL và sử dụng UDF hoặc các thủ tục được lưu trữ. Giả sử rằng bạn có một bảng AddressFields bao gồm các trường Latitude và Longitude. Thêm bảng đó vào tệp DBML của bạn và viết bất kỳ mã nào bạn muốn để đặt các trường Kinh độ và Vĩ độ. Sau đó, mã SQL bên dưới sẽ thêm trường Địa lý địa lý vào bảng đó và tạo trình kích hoạt trong cơ sở dữ liệu tự động đặt trường Địa lý dựa trên trường Vĩ độ và Kinh độ. Trong khi đó, đoạn mã dưới đây cũng tạo ra các UDF hữu ích khác và các thủ tục được lưu trữ:DistanceBetween2 (Tôi đã có một DistanceBetween) trả về khoảng cách giữa địa chỉ được thể hiện trong AddressField và một cặp vĩ độ / kinh độ được chỉ định; DistanceWithin trả về các trường khác nhau từ tất cả các Trường Địa chỉ trong khoảng cách một dặm được chỉ định; UDFDistanceWithin hoạt động giống như một hàm do người dùng xác định (hữu ích nếu bạn muốn nhúng hàm này vào một truy vấn lớn hơn); và UDFNearestNeighbors trả về các trường từ AddressField tương ứng với số lượng hàng xóm được chỉ định gần một điểm cụ thể. (Một lý do để sử dụng UDFNearestNeighbors là SQL Server 2008 sẽ không tối ưu hóa việc sử dụng chỉ mục không gian nếu bạn chỉ gọi thứ tự bằng cách gọi DistanceBetween2.)
Bạn sẽ cần tùy chỉnh điều này bằng cách thay đổi Trường địa chỉ thành bảng của bạn và tùy chỉnh các trường từ bảng đó mà bạn muốn trả về (xem mã xung quanh các tham chiếu đến AddressFieldID). Sau đó, bạn có thể chạy điều này trên cơ sở dữ liệu của mình và sao chép các thủ tục và UDF được lưu trữ kết quả vào DBML của bạn, sau đó bạn có thể sử dụng chúng trong các truy vấn. Nhìn chung, điều này cho phép bạn tận dụng chỉ số không gian của các điểm một cách khá dễ dàng.
-----------------------------------------------------------------------------------------
- [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] TẠO CHỈ SỐ
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] TẠO THỦ TỤC 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] TẠO TRIGGER TRÊN THAY ĐỔI / CHÈN GIÁ TRỊ LAT / DÀI ---> ĐẶT GEOCODE
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] TẠO PROC USP_SET_GEO_VALUE_INITIAL_LOAD ----> CHỈ CHẠY MỘT LẦN
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] Khoảng cách PROC HIỆN TẠI, trả về khoảng cách giữa hai điểm được chỉ định
- theo cặp tọa độ vĩ độ / kinh độ. --ALTER PROC Khoảng cáchBetween2
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
ĐI - KIỂM TRA
Khoảng cách giữa 2 12159,40.75889600, -73.99228900
- [8] TẠO THỦ TỤC USPDistanceWithin
- DANH SÁCH ĐỊA CHỈ TRỞ LẠI TỪ bảng AddressFields
NẾU TỒN TẠI (CHỌN tên TỪ sysobjects WHERE name ='USPDistanceWithin' AND type ='P') THỦ TỤC THẢ 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
ĐI
- THỬ NGHIỆM
- trong vòng 3 dặm USPDistanceWithin 38.90606200, -76.92943500,3GO - trong vòng 5 dặmUSPDistanceWithin 38.90606200, -76.92943500,5GO - trong vòng 10 dặmUSPDistanceWithin 38.90606200, -76.92943500,10
- [9] TẠO FUNCTION FNDistanceWithin
- DANH SÁCH ĐỊA CHỈ TRỞ LẠI TỪ bảng AddressFields
NẾU TỒN TẠI (CHỌN tên TỪ sysobjects WHERE name ='UDFDistanceWithin' AND type ='TF') CHỨC NĂNG DROP 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
ĐI
- THỬ NGHIỆM
--Kithin 3 Milesselect * từ udfdistancewithin (38.90606200, -76.92943500,3) Go-within 5 milelect * từ udfdistancewithin (38.90603
- [9] TẠO CHỨC NĂNG UDFNearestNeighbors
- DANH SÁCH ĐỊA CHỈ TRỞ LẠI TỪ bảng AddressFields
NẾU TỒN TẠI (CHỌN tên TỪ sysobjects WHERE name ='UDFNearestNeighbors' AND type ='TF') CHỨC NĂNG DROP UDFNearestNeighbors
GO
NẾU TỒN TẠI (CHỌN tên TỪ sysobjects WHERE name ='number' AND xtype ='u') Số lượng trong BẢNG HIỆU QUẢ
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
ĐI
- THỬ NGHIỆM
--50 người hàng xóm chọn * từ UDFNearestNeighbors (38.90606200, -76.92943500,50) ĐI - 200 người hàng xóm chọn * từ UDFNearestNeighbors (38.90606200, -76.92943500.200) ĐI