SQL 交叉应用性能问题
Posted
技术标签:
【中文标题】SQL 交叉应用性能问题【英文标题】:SQL Cross Apply Performance Issues 【发布时间】:2012-01-31 21:51:28 【问题描述】:我的数据库有一个目录,其中包含分布在美国各地的大约 2,000 个地点的邮政编码信息(我已将其与经度/纬度坐标相关联)。
我还有一个表格函数,它接受两个参数(ZipCode 和 Miles)来返回相邻邮政编码的列表(不包括搜索的相同邮政编码)
对于每个位置,我都在尝试获取相邻的位置 ID。因此,如果位置 #4 具有三个附近的位置,则输出应如下所示:
-
4 5
4 24
4 137
也就是说,位置 5、24 和 137 位于位置 4 的 X 英里范围内。
我最初尝试对我的函数使用交叉应用,如下所示:
SELECT A.SL_STORENUM,A.Sl_Zip,Q.SL_STORENUM FROM tbl_store_locations AS A
CROSS APPLY (SELECT SL_StoreNum FROM tbl_store_locations WHERE SL_Zip in (select zipnum from udf_GetLongLatDist(A.Sl_Zip,7))) AS Q
WHERE A.SL_StoreNum='04'
但是运行了 20 多分钟没有结果,所以我取消了它。我确实尝试在邮政编码中进行硬编码,它立即返回了一个列表
SELECT A.SL_STORENUM,A.Sl_Zip,Q.SL_STORENUM FROM tbl_store_locations AS A
CROSS APPLY (SELECT SL_StoreNum FROM tbl_store_locations WHERE SL_Zip in (select zipnum from udf_GetLongLatDist('12345',7))) AS Q
WHERE A.SL_StoreNum='04'
完成此附近地点列表的最有效方法是什么?请记住,虽然我在这里使用“04”作为示例,但我想对 2,000 个位置进行分析。
“udf_GetLongLatDist”是一个函数,它使用一些数学计算两个地理坐标之间的距离,并返回一个距离大于 0 的邮政编码列表。其中没有什么花哨的。
【问题讨论】:
附注与您的问题没有直接关系,但我们发现邮政编码的质心纬度/经度位置通常与邮政编码中大多数人居住的位置相距甚远。农村邮政编码。所以我们使用了两个位置,邮政编码质心纬度/经度,以及与邮政编码相关的主要城镇/城市的纬度/经度。如果这两个位置中的任何一个位于目标位置的 x 英里范围内,则认为邮政编码就在附近。 【参考方案1】:当您使用该函数时,您可能必须计算每一行的每个可能的距离。这就是为什么需要这么长时间。由于实际的物理位置通常不会移动,所以我们一直做的是预先计算每个邮政编码与其他邮政编码的距离(并且当我们添加新的可能的邮政编码时大约每月更新一次)。预先计算好距离后,您所要做的就是运行类似
的查询select zip2 from zipprecalc where zip1 = '12345' and distance <=10
【讨论】:
谢谢。这实际上是我创建一个预编译列表的目标,所以我只需要定期更新它。下面有人发布了一个运行速度比我快得多的查询。【参考方案2】:我们有类似的东西,并通过仅计算纬度在有界范围内的其他邮政编码的距离来优化它。因此,如果您想在@miles 中使用其他拉链,请使用
where latitude >= @targetLat - (@miles/69.2) and latitude <= @targetLat + (@miles/69.2)
那么您只是在计算其他邮政编码行的一小部分子集的大圆距离。我们发现这个速度足够快,不需要预先计算。
由于赤道和极点之间经度所代表的距离不同,因此不能对经度进行相同的处理。
【讨论】:
【参考方案3】:这里的其他答案涉及重新设计算法。我个人建议所有邮政编码的预先计算的地图相互对照。应该可以将此类优化嵌入到您现有的 udf 中,以最大程度地减少代码更改。
然而,查询的重构可能如下...
SELECT
A.SL_STORENUM, A.Sl_Zip, C.SL_STORENUM
FROM
tbl_store_locations AS A
CROSS APPLY
dbo.udf_GetLongLatDist(A.Sl_Zip,7) AS B
INNER JOIN
tbl_store_locations AS C
ON C.SL_Zip = B.zipnum
WHERE
A.SL_StoreNum='04'
此外,如果您可以确保 udf 是 INLINE 而不是 MULTI-STATEMENT,则 CROSS APPLY 的性能将大大受益。这允许 udf 内联扩展(类似宏)以获得更清晰的执行计划。
这样做还可以让您从 udf 返回其他字段。然后,优化器可以根据您是否实际使用这些字段,在计划中包含或排除这些字段。这样的示例将包括 SL_StoreNum
,如果它可以从 udf 中的查询轻松访问,那么就不需要最后一个连接...
【讨论】:
效果很好!它在大约 90 秒内运行了所有位置。你能解释一下为什么你的表现比我的好很多吗?我也会看看 udf 是如何配置的。 @user1181412 - 基本的区别是你使用了IN
,而我使用了JOIN
。那个我的将函数调用保持在与主表相同的范围内。我怀疑,这些意味着优化者对关系有更清晰的认识,并制定了更好的计划。特别是,如果它已经是 InLine,它可能会作为一个集合运行一次 udf。以上是关于SQL 交叉应用性能问题的主要内容,如果未能解决你的问题,请参考以下文章
在 SQL 中将 XML 文档转换为表格数据集的有效方法,因为随着 xml 的增长,交叉应用 xml 查询的性能呈指数级下降