rno*_*nko 7 performance sql-server dynamic-sql sql-server-2008-r2 query-performance
表Segments具有按 DEPARTMENT (VARCHAR(10)) 和 BDPID(VARCHAR(10)) 的索引。
第一个查询的执行时间为 34 秒
SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = 'DEP345'
AND seg.BDPID = c.BDPID
Run Code Online (Sandbox Code Playgroud)
当我将 DEPARTMENT 的参数移动到变量时,执行时间变为 1 秒。执行计划#2(快速)
DECLARE @dd VARCHAR(10)
SET @dd = 'DEP345'
SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID
Run Code Online (Sandbox Code Playgroud)
但我必须使用动态sql。当我将查询移动到 sp_execitesql 时,执行时间再次变为 34 秒。执行计划#3(慢)
EXECUTE sp_executesql
'SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID',
'@dd VARCHAR(10)',
@dd = 'DEP345'
Run Code Online (Sandbox Code Playgroud)
如何使用动态 sql 获得第二个查询的性能?
你遇到的是局部变量的诅咒。
简而言之,当您声明一个变量然后在查询中使用它时,SQL 无法嗅探该值。
它有时会根据变量的使用方式使用幻数(对于BETWEEN, >, >= , <, <=, <>.
对于等式搜索,使用密度向量(尽管此处将列定义为唯一事项)但底线是基数估计通常很远。这里有更多信息。
这是您的快速查询计划:
对于参数化的动态 SQL,可以嗅探该值,最终会得到一个完全不同的计划和完全不同的估计。
如果你想要一个快速而肮脏的选择,你可以使用 OPTIMIZE FOR UNKNOWN
EXECUTE sp_executesql
@stmt = N'SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID
OPTION (OPTIMIZE FOR (@dd UNKNOWN));',
@params N'@dd VARCHAR(10)', @dd = 'DEP345'
Run Code Online (Sandbox Code Playgroud)
这将为您提供密度向量估计,但这是一个笨拙的解决方案,我有点讨厌它。当您TOP 10从查询中删除 时,这当然会消失,您在评论中提到的内容仅适用于这篇文章。
我更希望看到你做的是一些索引调整。我敢打赌该Segment表正在为聚集索引而哭泣——HEAP 中的许多行通常是一个麻烦的迹象(转发的提取会毁了你的一天)。
这里使用的非聚集索引没有覆盖,这也严重拖累了这个查询。
您可以RID Lookup通过添加FINAL_SEGMENT为包含列来摆脱。