【问题标题】:Does WITH statement execute once per query or once per row?WITH 语句是每个查询执行一次还是每行执行一次?
【发布时间】:2013-01-23 05:50:48
【问题描述】:

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

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

如果这导致 100 行,我希望 Query1 只执行一次 - 而不是 100 次。如果该假设正确,则运行整个查询所花费的时间大致等于:运行 Query1 + 从 SomeTable 中选择 + 将 SomeTable 连接到 Query1 所花费的时间。

我的情况是:

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

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

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

【问题讨论】:

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


    【解决方案1】:

    例子:

    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 -> 实际执行计划),那么您将获得最后一个查询的执行计划:

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

    所以,这个问题的答案

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

    是both:每个查询一次(crt)和每行一次(prev:对于来自crt 的每一个)。

    为了优化这个查询,首先, 1)您可以尝试将CTE(MyCTE或Query)的结果存储到表变量或临时表中

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

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

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

    【讨论】:

    • 感谢您的详细回复。这就说得通了。就我而言,我没有两次加入 CTE,只是从普通表加入了一次 CTE。但我认为你所说的仍然适用。 SQL Server 没有将 CTE 视为临时表,这对我来说似乎很奇怪——我认为它是这样工作的。
    • A CTE 是一个本地 视图。因此,当使用时,CTE 将像任何其他视图一样展开。
    • 刚看到这篇文章试图弄清楚为什么我的 12 个 CTE 的查询很慢。每个人都有超过 8000 次处决。感谢您的提示!
    • @BillSambrone:很高兴这个答案对您有所帮助。谢谢你的美言。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    相关资源
    最近更新 更多