Ric*_*ick 5 performance sql-server performance-tuning
所以我正在调整这个查询,我很确定在这种情况下,我可以用左联接替换内部联接而不影响数据。但是,我不完全确定为什么这会更快。这是查询:
SELECT DISTINCT cl.NAME AS company_name,
cl.id AS company_id,
ep.Plan__c AS plan_id,
ep.Employee__c,
ep.id,
do.Subcategory__c,
ep.Plan_Type__c,
pt.SubType,
Sum((pt.[shares] * fvh.[ValuePerShare])) AS TotalValue,
ppe.Deferral_Option__c,
dt.Defer_type_Code,
do.Short_Code__c,
pt.ContributionYear
FROM dbo.ParticipantTrades pt WITH (NOLOCK)
INNER JOIN dbo.PayoutPathElection ppe WITH (NOLOCK)
ON pt.payoutPathElectionID = ppe.Id
INNER JOIN dbo.DeferralOption do WITH (NOLOCK)
ON ppe.Deferral_Option__c = do.id
INNER JOIN dbo.EmployeePlan ep WITH (NOLOCK)
ON pt.employeePlan = ep.Id
LEFT JOIN dbo.DeferralType dt WITH (NOLOCK)
ON pt.deferralType = dt.defID
INNER JOIN dbo.Fnc_lastfundvalue('2019-01-30') AS fvh
ON pt.fund = fvh.Fund
INNER JOIN dbo.Clients cl with (NOLOCK)
ON ep.Company__c = cl.Id
WHERE ep.Company__c = '0017000001WL1HfAAL' AND ep.Plan_Type__c LIKE '%' AND pt.tradeDate <= '2019-01-30'
Group by cl.NAME,
cl.id,
ep.Plan__c,
ep.Employee__c,
ep.id,
do.Subcategory__c,
ep.Plan_Type__c,
pt.SubType,
ppe.Deferral_Option__c,
dt.Defer_type_Code,
do.Short_Code__c,
pt.ContributionYear
Run Code Online (Sandbox Code Playgroud)
瓶颈在于表值函数连接 (Fnc_lastfundvalue)。我对为什么将其更改为左连接更快的预感是,它可以对连接重新排序,并减少对 tempdb 的溢出?这是将 INNER JOIN dbo.Fnc_lastfundvalue.. 更改为 LEFT JOIN dbo.Fnc_lastfundvalue.. 前后的查询计划。
之前(29 秒):https : //www.brentozar.com/pastetheplan/?id=Bk1Y-WqEE
之后(3 秒):https : //www.brentozar.com/pastetheplan/?id=B1hoW-544
注意:上面的执行计划是在开发箱上创建的。生产服务器仍在 SQL Server 2008 上。
看起来有些差异是因为先运行了慢速计划。
慢速计划显然是针对冷缓存运行的,因为它显示了额外的物理读取,并且PAGEIOLATCH_SH与快速案例相比,成功地累积了额外的 15 秒等待时间。
这看起来好像影响了 TVF 本身的执行(5.5与1.35快速情况相比,这需要几秒钟。)以及使用它的结果的更广泛的计划。
你在评论中说第二次执行需要11几秒钟。这仍然比快速计划(3.446秒)慢 3 倍,因此并不能解释所有的性能差异。
您遇到的主要问题是由于多语句 TVF 的默认估计不佳(100在 2014/2016 和1更早版本的兼容性级别中修复了行的猜测)。实际上,您的 TVF 返回1,715行。
在内部连接的情况下,因为内部连接是关联和可交换的,它可以灵活地重新排序并移动到树的最深部分。如果实际返回了一行,这将是有意义的,因为它可以提前减少计划中其他连接的行数。
执行计划如下。粉色注释是 XML 中的“实际经过时间 (ms)”。
由于 1 行估计,它通过加入dbo.ParticipantTrades并选择具有嵌套循环和查找的计划开始很糟糕。嵌套循环的经过时间为13.976秒(大概其中大部分时间都花在等待上游的溢出排序以从中请求行,而3.26查找本身和相关的 IO 等待物理读取时花费了数秒)。
923,646行是从连接和估计中发出的1423.18,这种错误估计在计划中通过 3 种排序向上传播,并且散列连接随着它的进行而溢出(由于行低估)
当您添加LEFT JOIN它时,它不能自由地重新排序,并且连接发生在计划中更高的位置(在有问题的INNER JOIN情况下它会造成更少的损害)。无论如何,外连接的语义在这里都有帮助。
虽然对来自 TVF 的行数的估计仍然是1这不会对它直接涉及的连接的估计产生不利影响(SQL Server 假设来自该连接的基数将与连接的其他子树,这实际上是发生了什么 - 外部连接无法减少这个数字,因为未连接的行仍然会通过 - 仅NULL用于fvh列)。
外连接计划中仍然存在基数估计错误,但幅度不同,它请求足够的内存授予以避免溢出到任何地方。
此基数估计问题已在具有交错执行的最新版本中得到解决。同时(正如您在评论中所说,它需要与 2008 兼容)您可以通过将 TVF 结果存储到#temp表中并加入该表以允许对中间结果(和列统计信息)进行计数来手动交错它考虑到。
| 归档时间: |
|
| 查看次数: |
7778 次 |
| 最近记录: |