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?
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?
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);
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...
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)
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.
I'm curious. Does anyone know why Microsoft say "...a CTE can be self-referencing and can be referenced multiple times in the same query." in MSDN at http://msdn.microsoft.com/en-us/library/ms190766.aspx.
If that were the case then this would work:-
WITH
CustomerTowns (CustomerID, CustomerName, Town)
AS
(
SELECT
c.CustomerID,
c.Name,
a.Town
FROM
dbo.tblCustomer c
INNER JOIN
dbo.tblAddress a
ON
c.DefaultAddressID = a.AddressID
)
-- Query the CTE rather than doing the whole query sever times.
SELECT * FROM CustomerTowns ct ORDER BY ct.CustomerName
SELECT * FROM CustomerTowns ct ORDER BY ct.Town -- Except you cannot reference a CTE more than once!