Mar*_*ith 173

这取决于.

首先

什么是通用表格?

(非递归)CTE与其他构造非常相似,这些构造也可以用作SQL Server中的内联表表达式.派生表,视图和内联表值函数.请注意,虽然BOL说CTE"可以被认为是临时结果集",但这是纯粹的逻辑描述.通常情况下,它本身并没有进行物质化.

什么是临时表?

这是存储在tempdb中的数据页上的行的集合.数据页可以部分或全部驻留在存储器中.此外,临时表可以被索引并具有列统计信息.

测试数据

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;
Run Code Online (Sandbox Code Playgroud)

例1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780
Run Code Online (Sandbox Code Playgroud)

计划1

在上面的计划中注意,没有提到CTE1.它只是直接访问基表,并被视为相同

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 
Run Code Online (Sandbox Code Playgroud)

通过将CTE具体化为中间临时表来进行重写将大大适得其反.

实现CTE定义

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
Run Code Online (Sandbox Code Playgroud)

将涉及将大约8GB的数据复制到临时表中,然后仍然存在从中进行选择的开销.

例2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 
Run Code Online (Sandbox Code Playgroud)

上面的例子在我的机器上大约需要4分钟.

1,000,000个随机生成的值中只有15行与谓词匹配,但昂贵的表扫描发生了16次以找到这些值.

在此输入图像描述

这将是实现中间结果的良好候选者.等效的临时表重写耗时25秒.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 
Run Code Online (Sandbox Code Playgroud)

随着计划

将查询的一部分中间实现到临时表有时可能是有用的,即使它只被评估一次 - 当它允许重新编译查询的其余部分时利用对具体化结果的统计信息.此方法的一个示例是SQL Cat文章何时分解复杂查询.

在某些情况下,SQL Server将使用假脱机来缓存中间结果,例如CTE,并避免必须重新评估该子树.这在(迁移的)Connect项中讨论提供强制以强制CTE或派生表的中间实现.但是,没有为此创建统计数据,即使假脱机行的数量与估计的数量大不相同,也不可能使进行中的执行计划动态适应响应(至少在当前版本中).自适应查询计划可能成为可能未来).

  • 这是回答实际问题的唯一答案(问哪个具有更好的性能而不是差异或哪个是你最喜欢的),并且它正确地回答了这个问题:"它取决于"是正确的答案.它也是支持数据解释的唯一答案,其他几个(票数高)明确声称一个比另一个好,没有参考或证明......要明确,所有这些答案也是**错误**.因为"这取决于" (28认同)
  • 这也是一个写得很好、参考文献很多的答案。真的是一流的。 (3认同)
  • 我喜欢强调这部分,我发现这是正确的 将查询部分的中间具体化到临时表中有时会很有用,即使它只评估一次 (2认同)

gbn*_*gbn 62

我会说他们是不同的概念,但不能说"粉笔和奶酪".

  • 临时表适用于重用或对一组数据执行多个处理过程.

  • CTE可用于递归或简单地提高可读性.
    并且,像视图或内联表值函数也可以像处理宏一样在主查询中进行扩展

  • 临时表是另一个表,其中包含一些围绕范围的规则

我存储了procs,我使用它们(和表变量)

  • 临时表还允许有时需要的索引甚至统计,而CTE则不允许. (12认同)
  • 我认为这个答案并不足以突出CTE可能导致糟糕表现的事实.我通常在dba.stackexchange上引用这个[answer](http://dba.stackexchange.com/a/13117/65699).如果我正在查找`cte vs临时表',那么你的问题在我的搜索引擎中排在第二位,所以恕我直言这个答案需要强调CTE更好的弊端.TL;链接答案的DR:*CTE绝不能用于表现.*.我同意这句话,因为我经历过CTE的缺点. (9认同)
  • 在这个答案中没有一个提到性能。 (3认同)
  • @TT。有趣。我发现CTE的表现要好得多 (2认同)

小智 47

CTE有其用途 - 当CTE中的数据很小并且与递归表中的情况一样具有强大的可读性改进.但是,它的性能肯定不比表变量好,当一个人处理非常大的表时,临时表明显优于CTE.这是因为您无法在CTE上定义索引,并且当您有大量需要与另一个表连接的数据时(CTE就像一个宏).如果要连接多个表,每个表中包含数百万行记录,则CTE的执行速度会比临时表差得多.

  • 我从自己的经历中看到了这一点.CTE的表现明显变慢. (9认同)
  • CTE的执行速度也较慢,因为结果没有缓存.因此,每次使用CTE时,它都会重新运行查询,计划和所有操作. (7认同)
  • 并且数据库引擎可能会选择重新运行查询,不仅是每个引用,而且是消费者查询的每个 _row_,作为相关子查询......如果不需要,您必须始终注意这一点。 (2认同)

mar*_*c_s 32

临时表始终在磁盘上 - 因此只要您的CTE可以保存在内存中,它很可能会更快(就像表变量一样).

但话又说回来,如果你的CTE(或临时表变量)的数据负载太大,它也会存储在磁盘上,所以没有什么大的好处.

一般来说,我比临时表更喜欢CTE,因为它在我使用它之后就消失了.我不需要考虑明确地删除它或任何东西.

所以,最后没有明确的答案,但就个人而言,我更喜欢CTE而不是临时表.

  • 对于SQLite和PostgreSQL,临时表*会自动删除(通常在会话结束时).我不知道其他DBMS. (2认同)
  • CTE 就像一个临时视图。AFAIK 数据未存储,因此没有任何内容保存在内存中或存储在磁盘上。重要说明,每次使用 CTE 时,查询都会再次运行。 (2认同)
  • 就我个人而言,我从未见过 CTE 在速度方面比 Temp 表更有效。使用临时表可以更轻松地进行调试 (2认同)

sel*_*raj 6

CTE不会占用任何物理空间.它只是我们可以使用join的结果集.

临时表是临时的.我们可以创建索引,约束就像普通表一样,我们需要定义所有变量.

临时表的范围仅在会话中.EX:打开两个SQL查询窗口

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp
Run Code Online (Sandbox Code Playgroud)

在第一个窗口中运行此查询,然后在第二个窗口中运行以下查询,您可以找到差异.

select * from #temp
Run Code Online (Sandbox Code Playgroud)

  • >>"它只是一个我们可以使用连接的结果集." - >这不准确.CTE不是"结果集",而是内联代码.SQL Server查询引擎将CTE代码解析为查询文本的一部分,并根据构建执行计划.CTE内联的想法是使用CTE的一大优势,因为它允许服务器创建"组合执行计划" (4认同)

小智 6

我两者都用过,但在大量复杂的程序中,我总是发现临时表更适合使用且更有条理。CTE 有其用途,但通常数据较小。

例如,我创建的 sproc 会在 15 秒内返回大量计算的结果,但将此代码转换为在 CTE 中运行,并且已经看到它运行超过 8 分钟以实现相同的结果。


Mar*_*arc 5

聚会迟到了,但是……

我的工作环境受到很大限制,需要支持一些供应商产品并提供报告等“增值”服务。由于政策和合同的限制,我通常不允许拥有单独的表/数据空间和/或创建永久代码的能力[它会变得更好一点,具体取决于应用程序]。

IOW,我通常无法开发存储过程或 UDF 或临时表等。我几乎必须通过我的应用程序界面完成所有操作(Crystal Reports - 添加/链接表、设置来自 CR 的 where 子句等。 )。一个小小的优点是 Crystal 允许我使用命令(以及 SQL 表达式)。一些通过常规添加/链接表功能效率不高的事情可以通过定义 SQL 命令来完成。我通过这种方式使用 CTE,并“远程”获得了非常好的结果。CTE 还有助于报告维护,不需要开发代码,交给 DBA 进行编译、加密、传输、安装,然后需要进行多级测试。我可以通过本地界面进行 CTE。

使用带有 CR 的 CTE 的缺点是,每个报告都是单独的。必须为每个报告维护每个 CTE。在我可以做 SP 和 UDF 的地方,我可以开发一些可供多个报告使用的东西,只需要链接到 SP 并传递参数,就像您在处理常规表一样。CR 并不擅长将参数处理到 SQL 命令中,因此 CR/CTE 方面可能会有所欠缺。在这些情况下,我通常尝试定义 CTE 以返回足够的数据(但不是所有数据),然后使用 CR 中的记录选择功能对其进行切片和切块。

所以...我投票支持 CTE(直到我获得数据空间)。


pur*_*has 5

我发现 CTE 具有出色性能的一个用途是,我需要将一个相对复杂的查询连接到几个表,每个表有几百万行。

我使用 CTE 首先选择基于索引列的子集,首先将这些表削减为每个表几千个相关行,然后将 CTE 连接到我的主查询。这极大地减少了我的查询的运行时间。

虽然 CTE 的结果没有缓存,并且表变量可能是更好的选择,但我真的只是想尝试一下并发现它们适合上述场景。


Mar*_*rti 5

因此,我分配给优化的查询是用SQL Server中的两个CTE编写的.花了28秒.

我花了两分钟将它们转换为临时表,查询耗时3秒

我在正在连接的字段上为临时表添加了一个索引,并将其降低到2秒

三分钟的工作,现在通过移除CTE,运行速度提高了12倍.我个人不会使用CTE,因为它们也更难以调试.

疯狂的事情是,CTE都只使用过一次,而且它们的指数仍被证明要快50%.