我正在创建一个托管在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语句,如下所示。通过创建纬度和经度的最小和最大界限,通过在每个方向上移动一些数字来完成。
我解释说:
最大纬度57.xxxxxx
原点经度14.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
答案 0 :(得分:1)
一个好的经验法则是数据库查询的执行时间取决于必须读取的磁盘页数。通常可以忽略CPU时间。
根据此规则,如果对磁盘页面数量产生影响,则建议的优化将缩短执行时间。如果纬度和经度上有索引,将允许跳过许多表行,因此会跳过许多磁盘页面。如果是这种情况,优化器肯定会在距离之前评估WHERE子句的那一部分。
如果没有索引可以帮助这两列,我怀疑你会看到很大的不同。
答案 1 :(得分:0)
您可以使用MS Management Studio分析查询时间,使用不同的地方运行大查询,它甚至会显示查询的哪个部分需要多长时间。
您可以单击CTRL + L:显示估计的执行计划 或CTRL + M:显示实际执行计划(运行时)
首先使用“边界”运行一次,然后再使用边界运行一次。 你将能够看到哪个更慢,然后再次尝试没有边界。
如果您没有足够的数据,可能无法看到差异。