SQL Query使用GEOGRAPHY选择距离匹配的记录

时间:2017-01-31 20:21:27

标签: sql sql-server

this thread上,我找到了这个例子:

DECLARE @source geography = 'POINT(-94.25 45.46)'
DECLARE @target geography = 'POINT(-94.19 45.57)'
SELECT (@source.STDistance(@target)/1000) * 0.62137

这准确地告诉我有 两点之间约8英里。这非常有帮助。但是,现在我想做的事情有点复杂。

我有一张表Criteria,如下所示:

ID    State    Zip    Lat     Long    Radius
------------------------------------------------
1       MN    56301   45.46  -94.25    25

有更多的记录,但这对我们的目的来说已经足够了。现在,我需要查询记录,其中有直接状态匹配,或直接Zip匹配,或范围匹配。所以......

DECLARE @CompareState VARCHAR(2) = NULL
DECLARE @CompareZip VARCHAR(5) = NULL
DECLARE @CompareLon DECIMAL = -94.19
DECLARE @CompareLat DECIMAL = 45.57

SELECT
    *
FROM
    Criteria c
WHERE 
    c.State = @CompareState
    OR c.Zip = @CompareZip
    OR (Distance between two sets of Lat and Long is <= c.Radius)

在上面的查询中,应返回ID为1的行。我正在努力学习语法。

2 个答案:

答案 0 :(得分:1)

知道了。

DECLARE @CompareState VARCHAR(2) = NULL
DECLARE @CompareZip VARCHAR(5) = NULL
DECLARE @CompareLon DECIMAL = -94.19
DECLARE @CompareLat DECIMAL = 45.57

SELECT
    *
FROM
    LeadSalesCampaignCriterias c
    JOIN LeadSalesCampaignCriterias c2 
        ON c.LeadSalesCampaignCriteriaID = c2.LeadSalesCampaignCriteriaID 
        AND c2.Latitude IS NOT NULL 
        AND c2.Longitude IS NOT NULL
WHERE 
    c.State = @CompareState
    OR c.Zip = @CompareZip
    OR 
    (
        ((geography::Point(c2.Latitude, c2.Longitude, 4326).STDistance(geography::Point(@CompareLat, @CompareLon, 4326))/1000) * 0.62137) < c.Radius
    )

老实说,我不知道4326是什么 请参阅:https://msdn.microsoft.com/en-us/library/bb933811.aspx

答案 1 :(得分:1)

我扼杀了你的答案并为了提高效率而改变了一点。

DECLARE @CompareState VARCHAR(2) = NULL
DECLARE @CompareZip VARCHAR(5) = NULL
DECLARE @CompareLon DECIMAL = -94.19
DECLARE @CompareLat DECIMAL = 45.57
-- you appear to be wanting to find things within 1 mile
-- the magic number 1609.34 is the number of meters in a mile
DECLARE @RangeDisk geography = geography::Point(@CompareLat, @CompareLon, 4326).STBuffer(1609.34);

SELECT
    *
FROM
    LeadSalesCampaignCriterias c
    JOIN LeadSalesCampaignCriterias c2 
        ON c.LeadSalesCampaignCriteriaID = c2.LeadSalesCampaignCriteriaID 
        AND c2.Latitude IS NOT NULL 
        AND c2.Longitude IS NOT NULL
WHERE 
    c.State = @CompareState
    OR c.Zip = @CompareZip
    OR geography::Point(c2.Latitude, c2.Longitude, 4326).STIntersects(@RangeDisk) = 1

其他一些说明。如果您可以更改表格以预先计算地理列,那么这将使这更好,因为您不必在where子句中动态转换它(该谓词将采用new_column.STIntersects(@RangeDisk) = 1的形式)。新列的空间索引将为查询的效率创造奇迹!

我也对自我加入感到有些困惑。 LeadSalesCampaignCriteriaID是表中的主键吗?如果是这样,我认为加入是不必要的(很可能会影响你的表现)。

最后,在你的自我回答中,你提到不知道4326的幻数是什么。它被称为空间参考ID(也称为SRID)。从本质上讲,历史上曾多次尝试对地球进行建模。当您从外部源获得地理要素的表示时,它们将在考虑其中一个系统的情况下创建。即使您正在创建整个布料,您也需要知道测量单位是什么(例如,当您计算距离时)。您可以在sys.spatial_reference_systems中看到SQL知道的SRID的属性。