【问题标题】:SQL Query to get three closest locationsSQL 查询以获取三个最近的位置
【发布时间】:2014-10-09 17:09:23
【问题描述】:

这是我的查询难题: - 我有一张客户表,上面有他们的经度和纬度 邮政编码。 - 我有一张供应商表,也有经度和 他们的邮政编码的纬度。 - 两者之间没有关系 表。 - 我有一个计算距离的用户定义函数 在两组经度和纬度之间。

我有以下查询,可以显示(最多)三个最近的供应商在 50 英里半径范围内的给定客户(dbo.latlngdistance 是用户定义的函数):

select top 3 dname1, dcity, dstate, dzip, dbo.latlngdistance(dlr.latitude, dlr.longitude, 
    (select lat from homeowner where ho_identifier = 16), 
    (select lng from homeowner where ho_identifier = 16)) AS SortDistance
from dlr
where dbo.latlngdistance(dlr.latitude, dlr.longitude, 
    (select lat from homeowner ho_identifier = 16), 
    (select lng from homeowner where ho_identifier = 16)) between 0 and 50
Order By SortDistance

我有另一个查询返回所有客户半径 50 英里内的所有供应商(我在此示例中限制了客户数量,因为此查询的性能很糟糕):

select ho.ho_identifier, ho.ho_firstname, ho.ho_lastname, ho.ho_email, dname1, dcity, dstate, dzip
from dlr
    cross join homeowner ho
where dbo.latlngdistance(dlr.latitude, dlr.longitude, 
    (select lat from homeowner ho2 where ho.ho_identifier = ho2.ho_identifier), 
    (select lng from homeowner ho3 where ho.ho_identifier = ho3.ho_identifier)) between 0 and 50
and ho.ho_identifier in (16, 17, 18, 19, 20)
Order By ho.ho_identifier

所以我的问题是,我如何为所有客户找到(最多)50 英里半径范围内最近的三个供应商?

【问题讨论】:

  • 尝试创建另一个用户定义的函数或存储过程来循环执行您的第一个查询?
  • 我应该提到我被困在 SQL Server 2000 上——我知道,对吧?
  • 这很糟糕,有办法通过使用多个连接来实现,但我认为这种方式不是一个好方法......
  • 如果您可以将数据库升级到过去十年内制作的内容,您可以使用空间数据类型,它内置支持确定 k 最近邻 (blogs.msdn.com/b/isaac/archive/2008/10/23/…)。

标签: sql sql-server sql-server-2000


【解决方案1】:

我会创建一个具有类似定义的视图:

SELECT 
    ho.ho_identifier, dlr.dname1, --dlr.id would be even better, but you didnt give the name of the id column
    dbo.latlngdistance(dlr.latitute, dlr.longtitude, ho.lat, ho.lng) AS distance
FROM dlr d
CROSS JOIN homeowner ho

鉴于此视图将被称为计算距离,您可以通过以下方式找到最近的供应商:

SELECT ho.ho_identifier, dlr.dname1
FROM homeowner ho
JOIN calculatedDistances dis
    ON dis.ho_identifier = ho.ho_identifier
WHERE 
    NOT EXISTS(
        SELECT 1
        FROM calculatedDistances nearer
        WHERE
            nearer.distance < dis.distance
            AND nearer.ho_identifier = ho.ho_identifier
    )

找到最接近的三个会更加棘手,并且可能涉及一些严重的性能问题。你可以这样做:

SELECT 
    dis.ho_identifier, dis.dname1
FROM calculatedDistances dis
LEFT JOIN calculatedDistances nearer
    ON nearer.ho_identifier = dis.ho_identifier
    AND nearer.distance < dis.distance
GROUP BY dis.ho_identifier, dis.dname1
HAVING ISNULL(COUNT(1), 0) < 3

再一次 - 如果您遇到性能问题,您可能需要将 calcutedDistance 存储在其他一些表中。

现在我住的地方已经很晚了,如果我犯了任何错误,非常抱歉。如果是这样,请评论此答案,我将在明天尝试纠正自己。

【讨论】:

  • 您建议的视图创建了笛卡尔连接,考虑到我正在处理的表的大小(+50,000 个供应商和 +26,000 个房主)会产生严重的性能问题(例如,我使用两分钟后单个 ho_identifier)。但我喜欢你前进的方向!顺便说一句,我确实有一个 dlr_identifier 列。
  • 这就是我一直在写的问题。另一方面 - 您的查询应该有非常相似的执行计划。也许您可以创建一个表格来保存函数的计算结果?
  • 好吧,我将视图限制在 0 到 50 英里之间,这有助于单个 ho_identifier 查询,但到目前为止查询 5 个 ho_identifiers 需要三分钟。顺便说一句,我正在使用最后一个查询来建议找到最接近的三个。
  • 另一件有助于提高性能的事情是重写视图以不使用标量函数,而是内联进行计算。帮助阅读:databasejournal.com/features/mssql/article.php/3845381/…
【解决方案2】:

这是一位前同事的建议:

我想我会尝试将目标房主收集到一个临时表中以保存内容,然后使用 SQL 按程序逐步执行每一步。

例如(请注意,我无法测试这些)

Select HO_Identifier, zip.lat, zip.lng INTO MyTempHO
From DBC_Homeowner
LEFT Join Zip on zip.zip = left(5,HO_Zip) AND Zip.ll = 'L'  //I think there might also be a function to get lat/lng from zip....
WHERE <Your homeowner selection criteria>

这应该给你一个更小的桌子来工作。检查是否填写了所有 Lat、Lng 字段,某些字段的拉链可能有问题。当然左(5只适用于美国,不适用于加拿大)

创建另一个表来保存结果,例如

CREATE TABLE MyResults (HO_Identifier int,DLR_Identifier int)

写一个循环遍历每一行,找到前3名的经销商,比如

//将所有这些作为一个语句运行

Declare @HO_Identifier int, @HO_Lat double, @HO_Lng double

Declare Cursor HO from select HO_Identifier , Lat, Lang from MyTempHO

OPEN HO
FETCH FROM HO Into @HO_Identifier, @HO_Lat, @HO_Lng 
While @@INDEX = 0
BEGIN
    Insert MyResults (@HO_Identifier, Dlr.dlr_Identifier) Select TOP 3 DLR_Identifier from dlr 
        where dbo.LAtLngDistance(@HO_Lat, @HO_Lng, dlr.lat, dlr.lng) < 100
        AND <Your other dlr criteria>
        AND dlr.lng between HO_Lng + <some negative amount to limit results> AND HO_Lng - <Same negative amount> //Note lng is negative!
        ORDER BY dbo.LAtLngDistance(@HO_Lat, @HO_Lng, dlr.lat, dlr.lng)

    FETCH NEXT FROM HO Into @HO_Identifier, @HO_Lat, @HO_Lng 
END

CLOSE HO
DEALLOCATE HO

【讨论】:

    猜你喜欢
    • 2018-07-09
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    相关资源
    最近更新 更多