【发布时间】: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