Pரத*_*ீப் 4 performance sql-server execution-plan sql-server-2012
考虑以下设置。涉及三个表#CCP_DETAILS_TEMP
,Period
并且ACTUALS_DETAILS
#CCP_DETAILS_TEMP
会有50000
记录,ACTUALS_DETAILS
可以有5000000
记录,period
表会有2000
记录
指数详情:
CREATE UNIQUE CLUSTERED INDEX IX_CCP_DETAILS_TEMP
ON #CCP_DETAILS_TEMP (CCP_DETAILS_SID)
CREATE NONCLUSTERED INDEX IXN_ACTUALS_DETAILS_PERIOD_SID_RS_MODEL_SID_CCP_DETAILS_SID_QUANTITY_INCLUSION
ON ACTUALS_DETAILS (PERIOD_SID, CCP_DETAILS_SID, RS_MODEL_SID, QUANTITY_INCLUSION)
INCLUDE( SALES, QUANTITY, DISCOUNT)
CREATE UNIQUE CLUSTERED INDEX IX_PERIOD
ON PERIOD (PERIOD_SID)
Run Code Online (Sandbox Code Playgroud)
我有一个要求,我为此编写了三种不同的方法来实现结果。现在我想知道哪个更好。
所有三个查询都或多或少地同时运行。我需要一些专家的建议,以了解哪一个会表现得更好。任何一种方法都有什么缺点吗
方法一: Outer Apply
花的时间: 4615 Milli Seconds
SELECT c.CCP_DETAILS_SID,
A.PERIOD_SID,
SALES,
QUANTITY
FROM #CCP_DETAILS_TEMP c
CROSS JOIN (SELECT PERIOD_SID
FROM BPIGTN_GAL_APP_DEV_ARM..PERIOD
WHERE PERIOD_SID BETWEEN 577 AND 624)A
OUTER apply (SELECT Sum(SALES),
Sum(QUANTITY)
FROM [DBO].[ACTUALS_DETAILS] ad
WHERE a.PERIOD_SID = ad.PERIOD_SID
AND ad.CCP_DETAILS_SID = c.CCP_DETAILS_SID
AND QUANTITY_INCLUSION = 'Y') oa (sales, quantity)
Run Code Online (Sandbox Code Playgroud)
查询统计:
表'时期'。扫描计数 1,逻辑读 2,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表“#CCP_DETAILS_TEMP”。扫描计数 16,逻辑读取 688,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“工作台”。扫描计数16,逻辑读807232,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。
表'ACTUALS_DETAILS'。扫描计数1200000,逻辑读3859053,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。
表“工作台”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表“工作台”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
SQL Server 执行时间:CPU 时间 = 36796 毫秒,已用时间 = 4615 毫秒。
SQL Server 执行时间:CPU 时间 = 0 毫秒,已用时间 = 0 毫秒。
方法二: Left Join
花的时间: 4293 Milli Seconds
SELECT c.CCP_DETAILS_SID,
A.PERIOD_SID,
Sum(SALES),
Sum(QUANTITY)
FROM #CCP_DETAILS_TEMP c
CROSS JOIN (SELECT PERIOD_SID
FROM BPIGTN_GAL_APP_DEV_ARM..PERIOD
WHERE PERIOD_SID BETWEEN 577 AND 624) a
LEFT JOIN [ACTUALS_DETAILS] ad
ON a.PERIOD_SID = ad.PERIOD_SID
AND ad.CCP_DETAILS_SID = c.CCP_DETAILS_SID
AND QUANTITY_INCLUSION = 'Y'
GROUP BY c.CCP_DETAILS_SID,
A.PERIOD_SID
Run Code Online (Sandbox Code Playgroud)
查询统计:
表'ACTUALS_DETAILS'。扫描计数 17,逻辑读取 37134,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表'时期'。扫描计数 1,逻辑读 2,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表“#CCP_DETAILS_TEMP”。扫描计数 16,逻辑读取 688,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“工作台”。扫描计数16,逻辑读807232,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。
表“工作文件”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表“工作台”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
SQL Server 执行时间:CPU 时间 = 7983 毫秒,已用时间 = 4293 毫秒。
SQL Server 执行时间:CPU 时间 = 0 毫秒,已用时间 = 0 毫秒。
方法 3:先聚合和左连接:
花的时间: 4200 Milli Seconds
SELECT c.CCP_DETAILS_SID,
A.PERIOD_SID,
SALES,
QUANTITY
FROM #CCP_DETAILS_TEMP c
CROSS JOIN (SELECT PERIOD_SID
FROM BPIGTN_GAL_APP_DEV_ARM..PERIOD
WHERE PERIOD_SID BETWEEN 577 AND 624) a
LEFT JOIN (SELECT CCP_DETAILS_SID,
PERIOD_SID,
Sum(SALES) SALES,
Sum(QUANTITY) QUANTITY
FROM [ACTUALS_DETAILS] ad
WHERE QUANTITY_INCLUSION = 'Y'
GROUP BY CCP_DETAILS_SID,
PERIOD_SID) ad
ON a.PERIOD_SID = ad.PERIOD_SID
AND ad.CCP_DETAILS_SID = c.CCP_DETAILS_SID
Run Code Online (Sandbox Code Playgroud)
查询统计:
表'ACTUALS_DETAILS'。扫描计数 17,逻辑读取 37134,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“工作台”。扫描计数16,逻辑读807232,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。
表“工作文件”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表'时期'。扫描计数 1,逻辑读 2,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表“#CCP_DETAILS_TEMP”。扫描计数 16,逻辑读取 688,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“工作台”。扫描计数 0,逻辑读 0,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
SQL Server 执行时间:CPU 时间 = 7731 毫秒,已用时间 = 4200 毫秒。
SQL Server 执行时间:CPU 时间 = 0 毫秒,已用时间 = 0 毫秒。
对于未来的问题,请使用Paste The Plan发布实际的执行计划
。我想我能够使用屏幕截图和您的STATISTICS
输出对所有相关细节进行逆向工程,但我可能弄错了一些事情。看起来您的计划正在以 16 的 DOP 运行,从 返回大约 50000 行,从 返回#CCP_DETAILS_TEMP
24 行PERIOD
。
在所有三种查询计划之间的连接#CCP_DETAILS_TEMP
,并PERIOD
以同样的方式进行,具有相同的STATISTICS
输出,并作为外部表的加盟ACTUALS_DETAILS
。看起来 SQL Server 正在为该连接做正确的事情,但它并不是那么有趣,所以我将跳过那部分。与你的比较无关。
相关的是 上的表访问模式ACTUALS_DETAILS
。所有三个查询都在覆盖索引上使用索引查找,但索引查找的执行方式不同。在第一个查询中,使用PERIOD_SID
和CCP_DETAILS_SID
列执行了 1200000 次搜索。在第二个和第三个查询中,仅使用 执行了 17 次搜索PERIOD_SID
。我相信所有的行都是用 获取的PERIOD_SID BETWEEN 577 AND 624
,所以索引查找可以有效地被认为是一个以 开头PERIOD_SID = 577
和结尾的并行索引扫描PERIOD_SID = 624
。这导致查询之间的 IO 存在很大差异:
表'ACTUALS_DETAILS'。扫描计数1200000,逻辑读3859053,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。
表'ACTUALS_DETAILS'。扫描计数 17,逻辑读取 37134,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
不一遍又一遍地阅读相同的页面有很大的好处。虽然伪扫描方法确实可以在技术上读取不需要的页面,但总体上执行的 IO 会少很多。我也认为 IO 差异直接导致第一个查询和其他两个查询之间 CPU 时间的巨大差异:36796 ms vs 7731 ms。当第一个查询运行时,它平均保持 9 个 CPU 完全繁忙,而第二个和第三个查询的繁忙 CPU 不到 2 个。这对于第一个查询来说是一个很大的缺点,您会在繁忙的系统上注意到它,或者如果您的查询被迫以较低的 DOP 运行。以我有限的经验APPLY
我注意到 SQL Server 查询优化器倾向于将其实现为带有索引查找的嵌套循环连接。这应该被视为轶事证据,我相信有例外,但它解释了您在这里看到的情况。
Queries 2 and 3 implement the join to ACTUALS_DETAILS
as a hash join. I assume the idea behind pushing the GROUP BY
into the ad
derived table was so that SQL Server would perform the aggregation early and you would join to fewer rows and aggregate fewer rows. However, SQL Server rewrote your second query to perform the aggregation early anyway. You can tell because the stream aggregate and hash match operators are to the right of the hash match (right outer join) operator in the second plan. As far as I can tell the second and third query plans are effectively the same, although the third plan does have a few extra 0% cost operators.
就我个人而言,我认为 4293 和 4200 毫秒的运行时间或 CPU 时间的 7983 和 7731 毫秒之间的差异在统计上没有意义。如果您多次运行查询,则第二个查询可能比第三个查询更快。我会使用你觉得更自然的查询风格。就我个人而言,我会使用第三个查询,因为它更好地代表了我希望优化器执行的操作,即尽可能早地执行聚合。
归档时间: |
|
查看次数: |
1964 次 |
最近记录: |