CTE 执行多次

CTE executed multiple times

我今天在 SQL Server 2016 上遇到了 CTE 运行ning 的问题,目前我使用 table 变量解决了它,但是我不确定行为是否如此是错的还是我读错了文档。

当你运行这个查询时:

with cte(id) as 
(
    select NEWID() as id
)
select * from cte
union all
select * from cte

我希望相同的 guid 是两倍,但是有 2 个不同的 guid。根据文档 (https://docs.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-ver15),它 "Specifies a temporary named result set"。然而,上面的例子表明,它不是一个结果集,而是在使用时执行。

这个帖子不是要寻找不同的方法,而是要检查它是否有问题。

常见的 table 表达式不是临时的 table、物化视图或缓存的结果集。它们只是表达式,可以多次计算(这意味着对于像 NEWID() 这样的函数,每次计算表达式时都会得到一个新值)。我在 this post:

的“小心糖”部分谈到了这一点

Even in very simple cases, you can see that a CTE that accesses a table once, but is referenced multiple times, ends up evaluating the CTE (and hence accessing the underlying table) multiple times.

并在此处解决类似问题:

Martin Smith 在这里有一个深刻而透彻的回答:

  • Which are more performant, CTE or temporary tables?

我确实理解许多人对 CTE 的工作方式做出假设,我很欣赏缓存 CTE 所提供的价值。这不是他们今天的工作方式,但您可以投票 this feedback item 来塑造未来的功能。

您也可以 ask for clarification in the official documentation - 但通常文档不会列出功能不能做的所有事情。一个常见的例子是,“为什么文档没有明确说明没有 ORDER BYSELECT * FROM table 不能保证以某种特定顺序输出?”或者,更抽象地说,“为什么我的车的车主手册没有告诉我这辆车不能飞?”

与此同时,如果每次引用它的来源时都希望 NEWID() 具有相同的值,则应使用#temp table、@table 变量,或本地@variable。在您的示例中,更改可能很简单:

declare @newid uniqueidentifier = NEWID();

with cte(id) as 
(
    select @newid as id
)
select * from cte
union all
select * from cte

示例:db<>fiddle