【问题标题】:Optimizing SQL WHERE for calculating distance between latitude and longitude positions优化 SQL WHERE 以计算经纬度位置之间的距离
【发布时间】:2016-08-24 22:32:55
【问题描述】:

我正在创建一个托管在 MS SQL 2012 服务器上的数据库。该数据库的主要功能是返回距原点一定距离内的结果。位置被存储为纬度/经度。

通过在 Stack Overflow 上阅读这里,我发现了一种非常好的方法来查询数据库以准确查找我正在寻找的内容,它就像一个魅力!但是我正在考虑一种可能的方法来优化它。

原始 SQL 查询

DECLARE @orig_lat DECIMAL(12, 9)
DECLARE @orig_lng DECIMAL(12, 9)

SET @orig_lat=56.xxxxxx 
SET @orig_lng=14.xxxxxx

DECLARE @orig geography = geography::Point(@orig_lat, @orig_lng, 4326);

SELECT *
FROM foobar
WHERE @orig.STDistance(geography::Point(foobar.latitude, foobar.longitude, 4326)) < 2000

我的猜测是这个查询对 foobar 表进行线性搜索,只返回匹配的列。但是,由于此表包含世界各地的位置,我想知道是否可以通过减少运行距离计算所需的行数来帮助数据库。我的猜测是这个计算对于服务器来说很繁重。

我知道请求的来源,我也知道点之间的最大距离永远不会大于 100 公里。

假设

由于我知道我不必在距起点 100 公里的范围内搜索整个世界,因此我可以改进 WHERE 语句,如下所示。通过在每个方向上将位置移动某个数字来创建纬度和经度的最小和最大界限。

我解释一下:

  • 原纬56.xxxxxx
  • 最小纬度 55.xxxxxx
  • 最大纬度 57.xxxxxx

  • 原点经度 14.xxxxxx

  • 最小经度 13.xxxxxx
  • 最大经度 15.xxxxxx

通过这样做,我在原点周围创建了一个大约 126 公里的区域。通过将其添加到 WHERE 语句中,我首先确保请求的位置在正确的范围内。之后,我运行距离计算以获得准确的距离。距离计算现在只针对最小和最大范围内的行而不是整个世界。

优化建议

DECLARE @orig_lat DECIMAL(12, 9)
DECLARE @orig_lng DECIMAL(12, 9)
DECLARE @orig_latMin DECIMAL(12, 9)
DECLARE @orig_latMax DECIMAL(12, 9)
DECLARE @orig_lngMin DECIMAL(12, 9)
DECLARE @orig_lngMax DECIMAL(12, 9)

SET @orig_lat=56.xxxxxx
SET @orig_lng=14.xxxxxx
SET @orig_latMin=55.xxxxxx 
SET @orig_latMax=57.xxxxxx
SET @orig_lngMin=13.xxxxxx
SET @orig_lngMax=15.xxxxxx

DECLARE @orig geography = geography::Point(@orig_lat, @orig_lng, 4326);

SELECT *
FROM foobar
WHERE ([latitude] > @orig_latMin
    AND [latitude] < @orig_latMax 
    AND [longitude] > @orig_lngMin 
    AND [longitude] < @orig_lngMax)
    AND @orig.STDistance(geography::Point(foobar.latitude, foobar.longitude,   4326)) < 2000

我不知道数据库的实现细节,但这会改善查询还是让它变得更糟?我的猜测是,这取决于 WHERE 语句的实际工作方式以及它执行操作的顺序。我希望边界检查将在距离计算之前运行,以减少完成距离计算的时间。

编辑

刚刚实施了建议的索引提案,结果如下。

没有索引:

  • 优化语句的成本为 0,025352

  • 如果没有优化语句,则成本为 0,025323

带索引:

  • 优化语句的成本为 0,0104057

  • 如果没有优化语句,则成本为 0,0253234

【问题讨论】:

  • 检查执行计划

标签: sql sql-server tsql


【解决方案1】:

一个好的经验法则是数据库查询的执行时间取决于必须读取的磁盘页数。 CPU时间通常可以忽略。

根据此规则,如果您提出的优化对磁盘页数有影响,那么它会提高执行时间。如果 latitudelongitude 上有一个索引允许跳过许多表行并因此跳过许多磁盘页,就会出现这种情况。如果是这样,优化器肯定会在距离之前评估 WHERE 子句的那部分。

如果没有对这两列有帮助的索引,我怀疑你会看到很大的不同。

【讨论】:

  • 如果我理解你是正确的。通过编辑 foobar 表并添加这样的索引。 CREATE INDEX index_position on foobar (latitude, longitude);应该改进查询?
  • 如果您还没有这些列的索引,那么添加一个并尝试一下。检查执行计划以确保它们真正被使用。
  • 顺便说一句:我不是 SQLServer 空间数据支持方面的专家。但是您应该能够将位置保存在 geography 列中,然后在该列上创建空间索引并对其运行优化查询(无需计算距离的边界矩形)。
  • 您的解决方案效果惊人!稍后我也会看看地理专栏。谢谢 :) 在主帖中查看测试结果。
【解决方案2】:

您可以使用 MS Management Studio 分析查询时间,运行具有不同位置的大型查询,它甚至会显示查询的哪一部分需要多少时间。

可以点击CTRL+L:显示预估的执行计划 或 CTRL+M:显示实际执行计划(运行时)

先用“边界”运行一次,然后再用边界运行一次。 您将能够看到哪个较慢,然后再试一次。

如果您没有足够的数据,则可能看不到差异。

【讨论】:

    猜你喜欢
    • 2011-04-20
    • 2012-10-13
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多