jer*_*ech 2 performance sql-server execution-plan query-performance
我想知道是否有任何方法可以重写查询中的表达式(只是表达式,而不是整个查询)以短路 THEN 阶段的无用评估?
演示数据:
CREATE TABLE #Docs (
ID INT NOT NULL
,DocType TINYINT NOT NULL
);
CREATE TABLE #DocsItems (
IDDocs INT NOT NULL
,Amount NUMERIC(19,6)
);
INSERT INTO #Docs(ID, DocType) VALUES(1,1),(2,1),(3,2),(4,2),(5,2),(6,2);
INSERT INTO #DocsItems(IDDocs,Amount) VALUES(3,50.),(3,25.),(3,33.),(4,44.),(4,123.),(6,11.);
Run Code Online (Sandbox Code Playgroud)
主题查询:
SELECT
-- expression
SumAmount = CASE
WHEN D.DocType <> 1 THEN (SELECT SUM(Amount) FROM #DocsItems WHERE IDDocs = D.ID)
END
FROM #Docs D
WHERE D.DocType = 1 -- so CASE condition evaluates to False
Run Code Online (Sandbox Code Playgroud)
如果我将查询(故意)重写为:
SELECT
-- expression
SumAmount = CASE
WHEN 2 = 1 /* rewrite*/ THEN (SELECT SUM(Amount) FROM #DocsItems WHERE IDDocs = D.ID)
END
FROM #Docs D
WHERE D.DocType = 1
Run Code Online (Sandbox Code Playgroud)
目前正在发生什么
运行查询时,不会在运行时评估表扫描、流聚合和计算标量运算符。
为什么会发生
apply NL 连接意味着对于 中的每一行#Docs
,返回#Docsitems
与谓词匹配的行。这个谓词应该是WHERE IDDocs = D.ID
但是EXPR1007
select(实际的 case 语句)旁边的计算标量运算符( EXPR1005
) 仅在Doctype <> 1
. 如您所知,这不可能发生,它们都会返回NULL
。
计算 NL 和 SELECT 之间的标量:
计算连接内侧的标量:
这一切似乎都是由于 CASE 语句的功能和删除文字的方式。( CASE 2 = 1
vs. 的区别CASE WHEN D.DocType <> 1
)
解决
如果您将查询更改为:
SELECT
-- expression
SumAmount =
(SELECT SUM(Amount) FROM #DocsItems WHERE IDDocs = D.ID AND D.DocType <> 1 )
FROM #Docs D
WHERE D.DocType = 1 -- so CASE condition evaluates to False
Run Code Online (Sandbox Code Playgroud)
你应该得到你想要的执行计划:
删除正在更改重写计划的 SELonLOJ 规则。
猜测
可以通过添加提示来恢复为接近 CASE WHEN 查询而应用的规则: OPTION( QUERYRULEOFF SELonLOJ )
SELECT
-- expression
SumAmount =
(SELECT SUM(Amount) FROM #DocsItems WHERE IDDocs = D.ID AND D.DocType <> 1 )
FROM #Docs D
WHERE D.DocType = 1 -- so CASE condition evaluates to False
OPTION( QUERYRULEOFF SELonLOJ );
Run Code Online (Sandbox Code Playgroud)
显示与 case 语句相同的情况(减去过滤器和左连接),但没有发生运行时消除。
&
case when
关闭它时似乎更接近的另一个规则是JoinPredNorm
SELECT
-- expression
SumAmount =
(SELECT SUM(Amount) FROM #DocsItems WHERE IDDocs = D.ID AND D.DocType <> 1 )
FROM #Docs D
WHERE D.DocType = 1 -- so CASE condition evaluates to False
OPTION( QUERYRULEOFF JoinPredNorm )
Run Code Online (Sandbox Code Playgroud)
在#Docs 表上按预期进行过滤。
归档时间: |
|
查看次数: |
382 次 |
最近记录: |