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

时间:2013-02-07 16:30:02

标签: sql-server performance sql-server-2008 common-table-expression with-statement

我对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行。

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

1 个答案:

答案 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 - >实际执行计划),那么您将获得上次查询的执行计划: enter image description here

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

所以,这个问题的答案

  

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

both:每次查询一次(crt),每行一次(prev:每次crt一次)。

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

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

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

当然,您可以尝试重写最终查询,而无需CTE之间的这种自联接。