CTE的奇怪行为

时间:2015-07-13 12:23:57

标签: sql-server common-table-expression

我刚回答:Generate scripts with new ids (also for dependencies)

我的第一次尝试是:

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  

第二个选择应该传递 - 至少我是这么认为 - “myNewFormID”中的两个值,每次三次......但它提供了6个不同的值。这意味着CTE的“NEWID()”是针对最终结果集的每一行完成的。我错过了什么?

2 个答案:

答案 0 :(得分:3)

您对CTE的理解是错误的。它们不仅仅是一个填充了查询结果的表变量 - 相反,它们本身就是一个查询。请注意,CTE可以递归使用 - 这对于表变量非常有用:)

来自MSDN:

  

公用表表达式(CTE)可以被认为是在单个SELECT,INSERT,UPDATE,DELETE或CREATE VIEW语句的执行范围内定义的临时结果集。 CTE类似于派生表,因为它不作为对象存储,并且仅在查询期间持续。与派生表不同,CTE可以是自引用的,并且可以在同一查询中多次引用。

"可以被认为"有点欺骗 - 确定,可以想到,但它结果集。当您仅使用纯函数时,您不会看到此表现形式,但正如您已经注意到的那样,newId 不是纯粹。实际上,它更像是一个命名子查询 - 在您的示例中,如果您只是将查询从CTE直接移动到from子句,您将获得相同的内容。

为了进一步说明这一点,您可以在CTE上添加另一个连接到查询:

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

您会看到frms2.myNewFormID包含不同的myNewFormIDs

请记住这一点 - 当您仅对非变化数据使用纯函数时,您只能将CTE视为结果集;换句话说,如果在可序列化的事务中执行相同的查询隔离级别两次将产生相同的结果集。

答案 1 :(得分:0)

NEWID()每次执行时都返回一个值。无论何时使用它,您都会获得一个新值 例如,

select top 5 newid()
from sys.tables
order by newid()

您不会看到它们的顺序,因为所选字段的生成值与“排序依据”字段不同