我想知道是否有任何方法可以重写查询中的 EXPRESSION(只是表达式,而不是整个查询)以短路 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.);
主题查询:
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
如果我将查询(故意)重写为:
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
目前正在发生的事情
运行查询时,不会在运行时评估表扫描、流聚合和计算标量运算符。
为什么会发生
apply NL 连接意味着对于 中的每一行
#Docs
,返回#Docsitems
与谓词匹配的行。这个谓词应该是WHERE IDDocs = D.ID
但是 select (实际的 case 语句)旁边的计算标量运算符 ( ) 仅在
EXPR1007
NL apply ( ) 的内侧调用标量函数。如您所知,这不可能发生,并且它们都返回。EXPR1005
Doctype <> 1
NULL
计算 NL 和 SELECT 之间的标量:
计算连接内侧的标量:
这一切似乎都是由于 CASE 语句的功能和删除文字的方式。(与 之间的
CASE 2 = 1
区别CASE WHEN D.DocType <> 1
)正在解决
如果您将查询更改为:
你应该得到你想要的执行计划:
删除正在更改重写计划的 SELonLOJ 规则。
猜测
可以通过添加提示来还原为更接近 CASE WHEN 查询而应用的规则:
OPTION( QUERYRULEOFF SELonLOJ )
显示与 case 语句相同的情况(减去过滤器和左连接),但没有发生运行时消除。
&
另一个似乎更接近
case when
关闭它的规则是JoinPredNorm
按预期在#Docs 表上进行过滤。