我对WITH语句(CTE)的理解是每个查询执行一次。使用这样的查询:
WITH Query1 AS ( ... )
SELECT *
FROM
SomeTable t1
LEFT JOIN Query1 t2 ON ...
如果这导致100行,我希望Query1
只执行一次 - 而不是100次。如果该假设正确,则运行整个查询所花费的时间大致等于:运行Query1
+从SomeTable
+加入SomeTable
选择Query1
我处于以下情况:
Query1
单独运行需要约5秒钟(400k行)。WITH
语句后,查询的其余部分和LEFT JOIN
需要大约15秒(400k行)。因此,当使用WITH
语句和LEFT JOIN
运行整个查询时,我本来希望查询能够及时完成,而不是让它继续运行一个小时,一旦停止,它只能达到11k行。
我显然是错的,但为什么呢?
答案 0 :(得分:5)
示例:
SET NOCOUNT ON;
SET IMPLICIT_TRANSACTIONS ON;
CREATE TABLE MyTable (MyID INT PRIMARY KEY);
GO
INSERT MyTable (MyID)
VALUES (11), (22), (33), (44), (55);
PRINT 'Test MyCTE:';
WITH MyCTE
AS (
SELECT *, ROW_NUMBER()OVER(ORDER BY MyID) AS RowNum
FROM MyTable
)
SELECT *
FROM MyCTE crt
LEFT JOIN MyCTE prev ON crt.RowNum=prev.RowNum+1;
ROLLBACK;
如果您在SSMS中运行以前的脚本(按Ctrl+M
- >实际执行计划),那么您将获得上次查询的执行计划:
在这种情况下,CTE对crt
别名执行一次,对prev
别名执行五次(!)次,对crt
的每一行执行一次。
所以,这个问题的答案
WITH语句是每个查询执行一次还是每行执行一次?
为both
:每次查询一次(crt
),每行一次(prev
:每次crt
一次)。
要优化此查询,首先,
1)您可以尝试将CTE(MyCTE
或Query
)的结果存储到表变量或临时表中
2)将此表的主键定义为连接列,
3)重写最终查询以使用此表变量或临时表。
当然,您可以尝试重写最终查询,而无需CTE之间的这种自联接。