why can't I access my CTE after I used it once

2019-01-13 18:54发布

问题:

My stored procedure looks like:

WITH MYCTE(....)
AS 
(
...
)

UPDATE ... (using my CTE)



DELETE ( using my CTE)  <---  says the object, my CTE, doesn't exist

Can I only use it once?

回答1:

In your example code, the CTE only persists for the UPDATE. If you need it to last longer, consider populating a #tempTable or @tableVariable with it, and then UPDATE and DELETE from those.

You may also augment your UPDATE to use an OUTPUT clause, like the following, so you can capture the affected rows. And use them in the DELETE, like here:

set nocount on
DECLARE @Table     table (PK int, col1 varchar(5))
DECLARE @SavedPks  table (PK int)

INSERT INTO @Table VALUES (1,'g')
INSERT INTO @Table VALUES (2,'g')
INSERT INTO @Table VALUES (3,'g')
INSERT INTO @Table VALUES (4,'g')
INSERT INTO @Table VALUES (5,'x')
INSERT INTO @Table VALUES (6,'x')
set nocount off

;WITH MYCTE
AS 
(
  SELECT PK, col1 FROM @Table
)
UPDATE MYCTE
    SET col1='xyz'
    OUTPUT INSERTED.PK
        INTO @SavedPks
    WHERE col1='g'

SELECT 'A',* FROM @Table

DELETE @Table
    WHERE PK IN (SELECT PK  from @SavedPks)

SELECT 'B',* FROM @Table

OUTPUT:

(4 row(s) affected)
     PK          col1
---- ----------- -----
A    1           xyz
A    2           xyz
A    3           xyz
A    4           xyz
A    5           x
A    6           x

(6 row(s) affected)

(4 row(s) affected)

     PK          col1
---- ----------- -----
B    5           x
B    6           x

(2 row(s) affected)


回答2:

Yep, the WITH MYCTE clause is not creating a permanent object to use in multiple queries afterwards: it's only modifying the one query you're adding that clause to! If you need very different functonality, consider, instead, using views...



回答3:

A CTE expression is only valid in its body. If you want to use it in other places, you should repeat the WITH clause too.

WITH MYCTE(....) AS ( ... ) 
UPDATE ... (using my CTE);  
-- a semicolon is necessary for statements followed by a CTE declaration

WITH MYCTE(....) AS ( ... )
DELETE ( using my CTE); 


回答4:

CTE don't create anything 'real'. They are merely a language element, a way to express a table expression that will be used, possible repeatedly, in a statement. When you say

WITH cteFoo AS (select ... from table where ...)
select ... from cteFoo where ...

is just another way of saying

select ... from (select ... from table where ....) as cteFoo where ...

CTE and derived tables are very similar, any query using derived tables can be rewriten as a CTE, and any non-recursive CTE can be rewritten as a query using derived tables. Personally, I much more preffer the CTE form as is more concise and easy to read.

CTEs allow for a table expression used multiple times to be declare only once:

  WITH cte AS (select ... from table where ...)
  select ... 
    from cte a join cte b on ...
  where ...

Compare this with the semantically similar derived table form:

select ... 
from (
   select ... from table where ...) as a
join (
   select ... from table where ...) as b
   on ...
where ...

The CTE is clearly more readable. But you must understand that the two forms are producing the same query. The CTE form might suggest that an intermediate result is created then the join is run on the intermediate result, but this is not true. The CTE form is compiled into exactly the same form as the derived table one, which makes clear the fact that the CTE's table expresion is run twice.