在DELETE中使用CTE不会执行

时间:2012-10-25 14:05:43

标签: sql sql-server sql-server-2008-r2 common-table-expression

以下是运行和运行的原始查询:

;WITH includedCs_cte
    AS
    (
    SELECT 
            x.PKey, 
            x.OKey,
            y.CKey 
    FROM  
            #Permissions x
            JOIN WHDATA.dbo.tb_DimC y
                    ON 
                    x.PKey = y.PKey AND
                    x.OKey = y.OKey
        )
, b_cte
    AS
    (
    SELECT
            i.OKey,
            i.PKey
    FROM 
                WHData.dbo.vw_FactCX b
                INNER JOIN includedCs_cte i
                        ON 
                        b.PKey = i.PKey AND
                        b.PlayCKey = i.CKey
    WHERE b.DateKey >= @myLAST28DAYS
    GROUP BY
            i.OKey,
            i.PKey
    )
, POK_cte
    AS
    (
    SELECT
            i.OKey,
            i.PKey
    FROM 
                WHData.dbo.vw_FactCY b
                INNER JOIN includedCs_cte i
                        ON 
                        b.PKey = i.PKey AND
                        b.PlayCKey = i.CKey
    WHERE b.DateKey >= @myLAST28DAYS
    GROUP BY
            i.OKey,
            i.PKey
    )
, includedOKeys
    AS
    (
    SELECT *
    FROM b_cte
    UNION
    SELECT * FROM POK_cte
    )
DELETE FROM #Permissions
FROM #Permissions p
WHERE NOT EXISTS
                    (
                    SELECT 1
                    FROM includedOKeys x
                    WHERE 
                            p.PKey = x.PKey AND
                            p.OKey = x.OKey     
                    )   

如果我将上面的内容更改为以下内容,则会在不到10秒的时间内运行。为什么这些执行方式如此不同?

;WITH includedCs_cte
    AS
    (
    SELECT 
            x.PKey, 
            x.OKey,
            y.CKey 
    FROM  
            #Permissions x
            JOIN WHDATA.dbo.tb_DimC y
                    ON 
                    x.PKey = y.PKey AND
                    x.OKey = y.OKey
        )
, b_cte
    AS
    (
    SELECT
            i.OKey,
            i.PKey
    FROM 
                WHData.dbo.vw_FactCX b
                INNER JOIN includedCs_cte i
                        ON 
                        b.PKey = i.PKey AND
                        b.PlayCKey = i.CKey
    WHERE b.DateKey >= @myLAST28DAYS
    GROUP BY
            i.OKey,
            i.PKey
    )
, POK_cte
    AS
    (
    SELECT
            i.OKey,
            i.PKey
    FROM 
                WHData.dbo.vw_FactCY b
                INNER JOIN includedCs_cte i
                        ON 
                        b.PKey = i.PKey AND
                        b.PlayCKey = i.CKey
    WHERE b.DateKey >= @myLAST28DAYS
    GROUP BY
            i.OKey,
            i.PKey
    )
, includedOKeys
    AS
    (
    SELECT *
    FROM b_cte
    UNION
    SELECT * FROM POK_cte
    )
SELECT *
INTO #includedOKeys
FROM includedOKeys
CREATE CLUSTERED INDEX ix_inclProdOper ON #includedOKeys(OKey, PKey)

DELETE FROM #Permissions
FROM #Permissions p
WHERE NOT EXISTS
        (
        SELECT 1
        FROM #includedOKeys x
        WHERE 
            p.PKey = x.PKey AND
            p.OKey = x.OKey     
        )   

1 个答案:

答案 0 :(得分:3)

使用时解析CTE。这意味着如果您在查询中使用它两次,它可能会被解析两次。 CTE 并不总是一次解析并缓存在内存中。 SQL Server可以根据需要自由执行查询。

在您的情况下,它可能比那更糟糕 - 因为您在EXISTS子句中使用它作为相关子查询,这是一个逐行操作。这可能意味着该计划导致CTE被解析为#permissions表的 EACH ROW !这很可能是所有时间都在进行的地方。 SSMS中的显示执行计划(Ctrl-L)是您的朋友。

检查此SQLFiddle,即使CTE仅创建3行,也显示NID的GUID相同。事实上,我们获得了18个不同的GUID。

with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;