Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Възможно ли е да се използва SqlGeography с Linq to Sql?

Ако всичко, което искате да правите с SqlGeography, е да проследявате точки и да се възползвате от пространствените индекси на SQL Server 2008, можете, както са отбелязали други, да скриете колоната си с пространствени данни от Linq към SQL и да използвате UDF или съхранени процедури. Да предположим, че имате таблица AddressFields, която включва полета за ширина и дължина. Добавете тази таблица към вашия DBML файл и напишете всеки код, който искате, който задава полетата за ширина и дължина. След това SQL кодът по-долу ще добави поле Geo geogarphy към тази таблица и ще създаде тригер в базата данни, който автоматично задава полето Geo въз основа на полетата Latitude и Longitude. Междувременно кодът по-долу също създава други полезни UDF и съхранени процедури:DistanceBetween2 (вече имах DistanceBetween) връща разстоянието между адреса, представен в AddressField, и определена двойка ширина/дължина; DistanceWithin връща различни полета от всички AddressFields в рамките на определено разстояние от мили; UDFDistanceWithin прави същото като дефинирана от потребителя функция (полезно, ако искате да вградите това в по-голяма заявка); и UDFNearestNeighbors връща полета от AddressField, съответстващи на посочения брой съседи, най-близки до определена точка. (Една от причините за използването на UDFNearestNeighbors е, че SQL Server 2008 няма да оптимизира използването на пространствен индекс, ако просто извикате поръчка чрез извикване на DistanceBetween2.)

Ще трябва да персонализирате това, като промените AddressFields към вашата таблица и персонализирате полетата от тази таблица, които искате да бъдат върнати (погледнете в кода около препратките към AddressFieldID). След това можете да стартирате това във вашата база данни и да копирате получените съхранени процедури и UDF във вашия DBML, след което можете да ги използвате в заявки. Като цяло това ви позволява да се възползвате сравнително лесно от пространствения индекс на точки.

-----------------------------------------------------------------------------------------

--[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] СЪЗДАВАНЕ НА ИНДЕКС

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] СЪЗДАВАНЕ НА ПРОЦЕДУРА USP_SET_GEO_VALUE PARA 1 ШИРОТА 2 ДЪЛЖИНА

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] СЪЗДАВАНЕ НА ЗАПУСКВАНЕ ПРИ ПРОМЯНА/ВМЪКВАНЕ НА СТОЙНОСТ НА LAT/LONG ---> ЗАДАЙТЕ ГЕОКОД

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 ----> ИЗПЪЛНЯВАНЕ САМО ЕДНО ПЪТ

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, което връща разстоянието между две посочени точки

--по координатни двойки ширина/дължина. --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

ОТПРАВИТЕ -- ТЕСТ

DistanceBetween2 12159,40,75889600,-73,99228900

--[8] СЪЗДАВАНЕ НА ПРОЦЕДУРА USPDistanceWithin

-- ВРЪЩА СПИСЪК С АДРЕСИ ОТ таблицата AddressFields

АКО СЪЩЕСТВУВА (ИЗБЕРЕТЕ име ОТ sysobjects WHERE име ='USPDistanceWithin' И тип ='P') ПРОЦЕДУРА ИЗПУСКАНЕ 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

ОТПРАВИ

--ТЕСТ

--в рамките на 3 милиUSPDistanceWithin 38.90606200,-76.92943500,3GO--в рамките на 5 милиUSPDistanceWithin 38.90606200,-76.92943500,5GO--в рамките на USPDistanceWithin 38.90606200,-76.92943500,5GO--в рамките на 10USPDistanceWithin,-76.92943500,5GO--в рамките на 10.92943500,5GO--в рамките на 10.92943500,5GO--в рамките на 10.92943500,5GO--в рамките на 10.92943500,3GO.

--[9] СЪЗДАВАНЕ НА ФУНКЦИЯ FNDistanceWithin

-- ВРЪЩА СПИСЪК С АДРЕСИ ОТ таблицата AddressFields

АКО СЪЩЕСТВУВА (ИЗБЕРЕТЕ име ОТ sysobjects WHERE име ='UDFDistanceWithin' И тип ='TF') ИЗПУСКАНЕ НА ФУНКЦИЯ 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

ОТПРАВИ

--ТЕСТ

--within 3 milesselect * from UDFDistanceWithin(38.90606200,-76.92943500,3)GO--within 5 milesselect * from UDFDistanceWithin( 38.90606200,-76.92943500,5)GO--within 10 mileselect * from UDFDistanceWithin( 38.90606200,-76.92943500,10)

--[9] СЪЗДАВАНЕ НА ФУНКЦИЯ UDFNearestNeighbours

-- ВРЪЩА СПИСЪК С АДРЕСИ ОТ таблицата AddressFields

АКО СЪЩЕСТВУВА (ИЗБЕРЕТЕ име ОТ sysobjects WHERE име ='UDFNearestNeighbors' И тип ='TF') ИЗПУСКАНЕ НА ФУНКЦИЯ UDFNearestNeighbors

GO

АКО СЪЩЕСТВУВА (ИЗБЕРЕТЕ име ОТ sysobjects WHERE име ='числа' И xtype ='u')ПРОПУСКАНЕ НА ТАБЛИЦАТА номера

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

ОТПРАВИ

--ТЕСТ

--50 изберете съседите * от UDFNearestNeighbors(38.90606200,-76.92943500,50)GO--200 изберете съседите * от UDFNearestNeighbors(38,90606200,-76,92943500)GO,20

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Избройте всички бази данни от свързан сървър в SQL Server (T-SQL примери)

  2. MAX срещу Топ 1 - кое е по-добро?

  3. Какво означава времеви печат в T-Sql в C#?

  4. Изберете размер на базата данни на SQL Server

  5. TIMEFROMPARTS() Примери в SQL Server (T-SQL)