sql >> Database >  >> RDS >> Sqlserver

Is het mogelijk om SqlGeography te gebruiken met Linq to Sql?

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



  1. Het uitvoeren van SQL-script via psql geeft syntaxisfouten die niet voorkomen in PgAdmin

  2. pas pager aan in psql

  3. Hoe jaar en maand uit de datum halen in PostgreSQL zonder de functie to_char() te gebruiken?

  4. Databaseopties/packs Gebruiksrapportage