有关 MS SQL CTE 的详细信息

Dav*_*ave -2 sql-server cte

我试图找出 MS CTE 实际上如何访问数据。尝试找出 CTE 在提取数据时是否实际上会运行得更慢,以及 CTE 提取数据的顺序是什么。CTE 是在调用视图时提取数据还是在创建结果集时的最后一个 select 语句中提取数据?CTE 需要多久调用一次数据库来获取额外数据?

Eri*_*ing 6

主要区别

公共表表达式和临时对象之间的主要区别在于CTE 不会具体化结果集,并且每次引用它们时都需要重新执行内部查询。我不会将@table 变量引入其中,因为它会带来额外的性能复杂性。

常见的表表达式问题

我使用这个演示作为便携式示例。这里不存在性能问题,因为它是单行表,但足以表达要点。

CREATE TABLE
    #t
(
    id int NOT NULL PRIMARY KEY
);

INSERT
    #t
(
    id
)
SELECT
    id = 1;
Run Code Online (Sandbox Code Playgroud)

我们有一个单行表,现在让我们用 CTE 来处理它:

WITH 
    t AS
(
    SELECT
        t.id
    FROM #t AS t
)
SELECT
    t.*
FROM t
JOIN t AS t2
    ON t2.id = t.id
JOIN t AS t3
    ON t3.id = t.id;
Run Code Online (Sandbox Code Playgroud)

生成的查询计划如下所示:

坚果

对于公共表表达式的每个连接,我们最终都会访问基表。如果您的 CTE 被多次引用,通常最好使用 #temp 表来具体化结果。在包含更复杂查询的公共表表达式中,这可能特别痛苦。

行目标

虽然行目标不会具体化公共表表达式结果,但它们(至少到 SQL Server 的当前版本)确实为其中的查询提供了优化栅栏。

作为其工作原理的示例,请看一下这两个查询:

SELECT
    c = COUNT_BIG(*)
FROM dbo.Users AS u
JOIN dbo.Posts AS p
    ON p.OwnerUserId = u.AccountId
JOIN dbo.Comments AS c
    ON c.PostId = p.ParentId
JOIN dbo.Votes AS v
    ON v.PostId = c.PostId
WHERE c.Score > 1000;

SELECT
    c = COUNT_BIG(*)
FROM dbo.Users AS u
JOIN dbo.Posts AS p
    ON p.OwnerUserId = u.AccountId
JOIN 
(
    SELECT TOP (2147483647)
        c.*
    FROM dbo.Comments AS c
    JOIN dbo.Votes AS v
        ON v.PostId = c.PostId
    WHERE c.Score > 1000
) AS c ON c.PostId = p.ParentId;
Run Code Online (Sandbox Code Playgroud)

以下是它们的查询计划:

第一个查询: 坚果

第二次查询: 坚果

由于 SQL Server 必须遵守TOP 运算符设置的行目标,因此连接顺序发生了变化。这里没有真正的性能差异,但您明白了。如果您想一起发生一件特定的事情,这可能是实现这一目标的一种方法,无需临时对象,但无需具体化。

管理复杂性

有关通过使用 #temp 表来调整滥用 Common Table 表达式的查询的示例,请查看我的这篇文章:

那里有一个视频,我无法在此处嵌入该视频,该视频逐步识别诸如公共表表达式中的不良基数估计之类的内容,并将每个视频用作实现结果的停止点。