sql >> データベース >  >> RDS >> Sqlserver

Linq to SqlでSqlGeographyを使用することは可能ですか?

    SqlGeographyで実行したいのがトラックポイントだけで、SQL Server 2008の空間インデックスを利用する場合は、他の人が指摘しているように、空間データ列をLinqからSQLに非表示にして、UDFまたはストアドプロシージャを使用できます。 LatitudeフィールドとLongitudeフィールドを含むテーブルAddressFieldsがあるとします。そのテーブルをDBMLファイルに追加し、LatitudeフィールドとLongitudeフィールドを設定する必要なコードを記述します。次に、以下のSQLコードは、Geo geogarphyフィールドをそのテーブルに追加し、LatitudeフィールドとLongitudeフィールドに基づいてGeoフィールドを自動的に設定するトリガーをデータベースに作成します。一方、以下のコードは、他の便利なUDFとストアドプロシージャも作成します。DistanceBetween2(すでにDistanceBetweenがあります)は、AddressFieldで表されるアドレスと指定された緯度/経度のペアの間の距離を返します。 DistanceWithinは、指定されたマイル距離内のすべてのAddressFieldsからさまざまなフィールドを返します。 UDFDistanceWithinは、ユーザー定義関数と同じように機能します(これをより大きなクエリに埋め込みたい場合に便利です)。 UDFNearestNeighborsは、特定のポイントに最も近い指定された数のネイバーに対応するAddressFieldからフィールドを返します。 (UDFNearestNeighborsを使用する理由の1つは、DistanceBetween2を呼び出してorderを呼び出すだけでは、SQL Server 2008が空間インデックスの使用を最適化しないことです。)

    これをカスタマイズするには、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] CREATE 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] CREATE PROCEDURE 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]緯度/経度の値の変更/挿入時にトリガーを作成--->地理コードを設定

    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 PROCUSP_SET_GEO_VALUE_INITIAL_LOAD---->1回限りの実行

    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、指定された2点間の距離を返します

    -緯度/経度の座標ペアによる。 --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--テスト

    DistanceBetween2 12159,40.75889600、-73.99228900

    -[8] CREATE PROCEDURE USPDistanceWithin

    -AddressFieldsテーブルからアドレスのリストを返します

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

    -テスト

    -3マイル以内USPDistance38.90606200以内-76.92943500,3GO-5マイル以内USPDistance38.90606200以内-76.92943500,5GO-10マイル以内USPDistance38.90606200以内-76.92943500,10

    --[9] CREATE FUNCTION FNDistanceWithin

    -AddressFieldsテーブルからアドレスのリストを返します

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

    GO

    -テスト

    --3マイル以内*UDFDistanceWithin(38.90606200、-76.92943500,3)GOから-5マイル以内* UDFDistanceWithin(38.90606200、-76.92943500,5)GO-10マイル以内* UDFDistanceWithin(38.90606200、-76.92943500,10)

    -[9] CREATE FUNCTION UDFNearestNeighbors

    -AddressFieldsテーブルからアドレスのリストを返します

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

    GO
    

    IF EXISTS(SELECT name FROM sysobjects WHERE name ='numbers' AND xtype ='u')DROP TABLE numbers

    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

    -テスト

    --50のneighborsselect*from UDFNearestNeighbors(38.90606200、-76.92943500,50)GO--200のneighborsselect * from UDFNearestNeighbors(38.90606200、-76.92943500,200)GO



    1. PostgreSQL-異なるタイムゾーンで日付をレンダリングする方法は?

    2. fe_sendauth:パスワードが提供されていません

    3. SSMSのインストール方法

    4. Oracleでクエリ結果をコンマ区切りリストとして返す