3

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

WITH Query1 AS ( ... )
SELECT *
FROM
    SomeTable t1
    LEFT JOIN Query1 t2 ON ...

如果这导致 100 行,我希望Query1它只执行一次 - 而不是 100 次。如果该假设是正确的,则运行整个查询所花费的时间大致等于执行以下操作所花费的时间: run Query1+ select from SomeTable+ join SomeTableto Query1

我处于以下情况:

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

WITH因此,当使用语句和原地运行整个查询时LEFT JOIN,我希望查询能够及时完成,而不是让它运行了一个多小时,一旦停止,它只得到了 11k 行.

我显然错了,但为什么呢?

4

1 回答 1

6

例子:

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 对 alias 执行一次,对crtalias 执行五 (!) 次prev,对于 from 中的每一行执行一次crt

所以,这个问题的答案

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

both:每个查询一次(crt)和每行一次(prev:一次 for each for from crt)。

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

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

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

当然,您可以尝试在 CTE 之间没有这种自连接的情况下重写最终查询。

于 2013-02-08T00:21:46.840 回答