Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Czy można używać SqlGeography z Linq to Sql?

Jeśli wszystko, co chcesz zrobić z SqlGeography, to śledzić punkty i korzystać ze wskaźników przestrzennych SQL Server 2008, możesz, jak zauważyli inni, ukryć kolumnę danych przestrzennych od Linq do SQL i użyć UDF lub procedur składowanych. Załóżmy, że masz tabelę AddressFields, która zawiera pola szerokości i długości geograficznej. Dodaj tę tabelę do pliku DBML i napisz dowolny kod, który ustawia pola szerokości i długości geograficznej. Następnie poniższy kod SQL doda pole Geo geogarphy do tej tabeli i utworzy wyzwalacz w bazie danych, który automatycznie ustawia pole Geo na podstawie pól Latitude i Longitude. Tymczasem poniższy kod tworzy również inne przydatne UDF i procedury składowane:DistanceBetween2 (ja miałem już DistanceBetween) zwraca odległość między adresem reprezentowanym w AddressField a określoną parą szerokość/długość geograficzna; DistanceWithin zwraca różne pola ze wszystkich AddressFields w określonej odległości mili; UDFDistanceWithin działa tak samo, jak funkcja zdefiniowana przez użytkownika (przydatne, jeśli chcesz osadzić to w większym zapytaniu); a UDFNearestNeighbors zwraca pola z pola adresu odpowiadające określonej liczbie sąsiadów najbliższych danemu punktowi. (Jednym z powodów używania UDFNearestNeighbors jest to, że SQL Server 2008 nie zoptymalizuje wykorzystania indeksu przestrzennego, jeśli po prostu wywołasz zamówienie, wywołując DistanceBetween2.)

Musisz to dostosować, zmieniając pola AddressFields na swoją tabelę i dostosowując pola z tej tabeli, które chcesz zwrócić (spójrz w kodzie wokół odwołań do AddressFieldID). Następnie możesz uruchomić to w swojej bazie danych i skopiować wynikowe procedury składowane i UDF do swojego DBML, a następnie użyć ich w zapytaniach. Ogólnie rzecz biorąc, pozwala to dość łatwo wykorzystać przestrzenny indeks punktów.

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

--[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] UTWÓRZ INDEKS

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] UTWÓRZ PROCEDURĘ USP_SET_GEO_VALUE PARA 1 LATITUDE 2 DŁUGOŚĆ

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] UTWÓRZ WYZWALANIE PRZY ZMIANIE WARTOŚCI SZER./DŁUG./WSTAW ---> USTAW GEOKOD

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] UTWÓRZ PROC USP_SET_GEO_VALUE_INITIAL_LOAD ----> TYLKO JEDEN URUCHOM

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, który zwraca odległość między dwoma określonymi punktami

--przez pary współrzędnych szerokość/długość geograficzna. --ALTER PROC odległość między 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

PRZEJDŹ--TESTUJ

Odległość między 2 12159,40.75889600,-73.99228900

--[8] UTWÓRZ PROCEDURĘ USPDdystans w ciągu

-- ZWRACA LISTĘ ADRESÓW Z tabeli AddressFields

IF EXISTS (SELECT nazwa FROM sysobjects WHERE nazwa ='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

Idź

--TEST

--w promieniu 3 milOdległośćUSPDW granicach 38.90606200,-76.92943500,3GO--w promieniu 5 milOdległośćUSPDW granicach 38.90606200,-76.92943500,5GO--w promieniu 10 milOdległośćUSPDW granicach 38.90606200,-76.92943500,10

--[9] UTWÓRZ FUNKCJĘ FNDodległośćW obrębie

-- ZWRACA LISTĘ ADRESÓW Z tabeli AddressFields

IF EXISTS (SELECT nazwa FROM sysobjects WHERE nazwa ='UDFDistanceWithin' AND typ ='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

Idź

--TEST

--w promieniu 3 milselect * od UDFDistanceWithin(38.90606200,-76.92943500,3)GO--w promieniu 5 milselect * od UDFDistanceWithin(38.90606200,-76.92943500,5)GO--w promieniu 10 milselect * od UDFDistanceWithin(38.90606200,-76.92943500,10)

--[9] UTWÓRZ FUNKCJĘ UDFNajbliżsi sąsiedzi

-- ZWRACA LISTĘ ADRESÓW Z tabeli AddressFields

IF EXISTS (SELECT nazwa FROM sysobjects WHERE nazwa ='UDFNearestNeighbors' AND type ='TF')DROP FUNCTION UDFNearestNeighbors

GO

IF EXISTS (SELECT nazwa FROM sysobjects WHERE nazwa ='liczby' AND xtype ='u')DROP TABLE numery

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

Idź

--TEST

--50 sąsiadówwybierz * z UDFNearestNeighbors(38,90606200,-76.92943500,50)GO--200 sąsiadówwybierz * z UDFNearestNeighbors(38.90606200,-76.92943500,200)GO



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Typy kursorów programu SQL Server — Kursor ZESTAWU KLUCZY | Samouczek SQL Server / Samouczek TSQL

  2. Dynamiczna tabela przestawna w SQL Server

  3. Jak używać sortowania UTF-8 w bazie danych SQL Server?

  4. Część czasowa pola DateTime w SQL

  5. Jak zmienić bieżący format daty w SQL Server (T-SQL)