Reputation: 67291
I just answered this: Generate scripts with new ids (also for dependencies)
My first attempt was this:
DECLARE @Form1 UNIQUEIDENTIFIER=NEWID();
DECLARE @Form2 UNIQUEIDENTIFIER=NEWID();
DECLARE @tblForms TABLE(id UNIQUEIDENTIFIER,FormName VARCHAR(100));
INSERT INTO @tblForms VALUES(@Form1,'test1'),(@Form2,'test2');
DECLARE @tblFields TABLE(id UNIQUEIDENTIFIER,FormId UNIQUEIDENTIFIER,FieldName VARCHAR(100));
INSERT INTO @tblFields VALUES(NEWID(),@Form1,'test1.1'),(NEWID(),@Form1,'test1.2'),(NEWID(),@Form1,'test1.3')
,(NEWID(),@Form2,'test2.1'),(NEWID(),@Form2,'test2.2'),(NEWID(),@Form2,'test2.3');
--These are the originalIDs
SELECT frms.id,frms.FormName
,flds.id,flds.FieldName
FROM @tblForms AS frms
INNER JOIN @tblFields AS flds ON frms.id=flds.FormId ;
--The same with new ids
WITH FormsWithNewID AS
(
SELECT NEWID() AS myNewFormID
,*
FROM @tblForms
)
SELECT frms.myNewFormID, frms.id,frms.FormName
,NEWID() AS myNewFieldID,flds.FieldName
FROM FormsWithNewID AS frms
INNER JOIN @tblFields AS flds ON frms.id=flds.FormId
The second select should deliver - at least I thought so - two values in "myNewFormID", each three times... But it comes up with 6 different values. This would mean, that the CTE's "NEWID()" is done for each row of the final result set. What am I missing?
Upvotes: 1
Views: 243
Reputation: 63732
Your understanding of CTEs is wrong. They are not simply a table variable that's filled with the results of the query - instead, they are a query on their own. Note that CTEs can be used recursively - this would be quite a sight with table variables :)
From MSDN:
A common table expression (CTE) can be thought of as a temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE, or CREATE VIEW statement. A CTE is similar to a derived table in that it is not stored as an object and lasts only for the duration of the query. Unlike a derived table, a CTE can be self-referencing and can be referenced multiple times in the same query.
The "can be thought" of is a bit deceiving - sure, it can be thought of, but it's not a result set. You don't see this manifesting when you're only using pure functions, but as you've noticed, newId
is not pure. In reality, it's more like a named subquery - in your example, you'll get the same thing if you just move the query from the CTE to the from
clause directly.
To illustrate this even further, you can add another join on the CTE to the query:
WITH FormsWithNewID AS
(
SELECT NEWID() AS myNewFormID
,*
FROM @tblForms
)
SELECT frms.myNewFormID, frms.id,frms.FormName
,NEWID() AS myNewFieldID,flds.FieldName,
frms2.myNewFormID
FROM FormsWithNewID AS frms
INNER JOIN @tblFields AS flds ON frms.id=flds.FormId
left join FormsWithNewID as frms2 on frms.id = frms2.id
You'll see that the frms2.myNewFormID
contains different myNewFormIDs.
Keep this in mind - you can only treat the CTE as a result set when you're only using pure functions on non-changing data; in other words, if executing the same query in a serializable transaction isolation level twice will produce the same result sets.
Upvotes: 3
Reputation: 6612
NEWID() returns a value every time it is executed. Whenever you use it you get a new value For example,
select top 5 newid()
from sys.tables
order by newid()
You will not see them order by because the selected field is produced with different values than the Order By field
Upvotes: 0