WITH语句是每个查询执行一次还是每行执行一次?

Jos*_* M. 2 sql-server performance with-statement common-table-expression sql-server-2008

我对WITH语句(CTE)的理解是每个查询执行一次.使用这样的查询:

WITH Query1 AS ( ... )
SELECT *
FROM
    SomeTable t1
    LEFT JOIN Query1 t2 ON ...
Run Code Online (Sandbox Code Playgroud)

如果这导致100行,我希望Query1它只执行一次 - 而不是100次.如果这个假设是正确的,运行整个查询所需的时间大致等于带到时间:跑Query1+选择SomeTable+加入SomeTableQuery1.

我处于以下情况:

  • Query1 单独运行需要约5秒(400k行).
  • 删除WITH语句后,查询的其余部分LEFT JOIN需要~15秒(400k行).

因此,当使用WITH语句和LEFT JOIN就地运行整个查询时,我会期望查询及时完成,而我让它运行超过一个小时,一旦停止它只能达到11k行.

我显然是错的,但为什么呢?

Bog*_*ean 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;
Run Code Online (Sandbox Code Playgroud)

如果您在SSMS中运行以前的脚本(按Ctrl+M- >实际执行计划),那么您将获得上次查询的执行计划: 在此输入图像描述

在这种情况下,CTE对crt别名执行一次,对别名执行五(!)次prev,对于每一行执行一次crt.

那么,这个问题的答案

WITH语句是每个查询执行一次还是每行执行一次?

both:每个查询(crt)一次,每行prev一次(每次为一次crt).

要优化此查询,首先,1)您可以尝试将CTE(MyCTEQuery)的结果存储到表变量或临时表中

2)将此表的主键定义为连接列,

3)重写最终查询以使用此表变量或临时表.

当然,您可以尝试重写最终查询,而无需在CTE之间进行此自连接.