通用表表达式(CTE)和临时表有什么区别?我什么时候应该使用另一个?

CTE

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

临时表

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
有帮助吗?

解决方案

这很广泛,但是我会尽可能地给您一个答案。

ctes ...

  • 不可索引(但可以在引用对象上使用现有索引)
  • 不能有约束
  • 本质上是一次性 VIEWs
  • 一直存在直到下一个查询运行
  • 可以是递归的
  • 没有专用的统计数据(依靠基础对象的统计数据)

#temp表...

  • 是tempdb中存在的真实物质表
  • 可以索引
  • 可以有限制
  • 坚持当前联系的生活
  • 可以通过其他查询或子过程引用
  • 具有引擎生成的专用统计数据

至于何时使用,它们具有非常不同的用例。如果您的结果非常大,或者需要多次参考,请将其放入 #temp 桌子。如果需要递归,是一次性的,或者只是为了简化逻辑上的东西, CTE 首选。

CTE 应该 切勿用于性能. 。您几乎永远不会使用CTE加快速度,因为这只是一次性视图。您可以与他们一起做一些整洁的事情,但是加快查询并不是其中之一。

其他提示

编辑:

请参阅下面的马丁评论:

CTE并未将其作为内存中的表格实现。这只是封装查询定义的一种方式。在OP的情况下,它将被内衬,并且与仅仅执行 SELECT Column1, Column2, Column3 FROM SomeTable. 。在大多数情况下,它们没有提前实现,这就是为什么这没有行回来的原因 WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, ,还要检查执行计划。尽管有时可能会破解启动线轴的计划。有一个连接项目,要求为此提示。 - 马丁·史密斯(Martin Smith)2012年2月15日在17:08


原始答案

CTE

在MSDN上了解更多信息

CTE创建了用于内存中的表,但仅适用于之后的特定查询。使用递归时,这可能是有效的结构。

您可能还需要考虑使用表变量。这是使用的 作为 使用临时表,可以多次使用,而无需重新对每个联接重新介绍。另外,如果您现在需要持续一些记录,请在下一个选择之后添加更多记录,在另一个OP之后添加一些记录,然后仅返回那些记录,那么这可以是一个方便的结构,就像它一样执行后需要删除。通常只是句法糖。但是,如果将行计数保持在低位,则永远不会实现磁盘。看 SQL Server中的温度表和表变量有什么区别? 更多细节。

临时表

在MSDN上阅读更多信息 - 向下滚动约40%

临时表实际上是在磁盘上创建的表,只是在每个人都知道可以删除的特定数据库中。当这些桌子不再需要时销毁它们是一个好的开发人员的责任,但DBA也可以擦除它们。

临时桌有两个品种:本地和全球。在MS SQL Server方面,您使用 #tableName 本地的名称,以及 ##tableName 全局的名称(请注意将单个或双#用作识别特征的使用)。

请注意,使用临时表,与表变量或CTE相比,您可以应用索引等,因为这些索引是正常含义的合法表。


通常,如果我已经有一个小数据集,并且想快速为小东西脚本脚本脚本脚本脚本,则我会使用临时表进行更长或更大的查询以及CTE或表变量。经验和其他人的建议表明,您应该使用CTE,而从中返回少量行的地方。如果您的数量很大,则可能会从温度表上的索引能力中受益。

接受的答案 这里说“ CTE绝不应该用于性能” - 但这可能会误导。在CTES与临时表的上下文中,我刚刚完成了从一组存储的Procs中删除一堆垃圾,因为某些Doofus一定认为使用临时表很少或没有开销。我把很多东西都推到了ctes中, 除了 那些在整个过程中合法将重复使用的人。所有指标的表现都获得了约20%的表现。然后,我着手删除试图实施递归处理的所有光标。这是我看到最大收益的地方。我最终将响应时间削减了十倍。

CTES和TEMP表确实具有不同的用例。我只想强调,虽然不是灵丹妙药,但CTE的理解和正确使用可以导致代码质量/可维护性和速度的真正出色的改进。由于我对它们有所处理,因此我将临时桌子和光标视为SQL处理的巨大弊端。现在几乎所有内容都可以使用表变量和CTE。我的代码更清洁,更快。

可以在查询中反复调用CTE,并在每次引用时都会对CTE进行评估 - 此过程可以递归。如果仅引用一次,则表现很像子查询,尽管CTE可以被参数化。

临时表在物理上持续存在,可能会被索引。实际上,查询优化器还可以持续幕后的中间连接或子查询结果,例如在启动操作中,因此,绝对不太认真地将CTE的结果持续到磁盘上。

IIRC表变量(另一方面)始终是内存结构。

temp表是tempdb中的真实对象,但是CTE只是一种围绕复杂查询的包装器,可以一步一步简化组织递归语法。

使用CTE的主要原因是访问窗口功能,例如 row_number() 还有其他。

这意味着您可以非常快速有效地完成每个组的第一行或最后一行 - 在大多数实际情况下,比其他方式更有效.

with reallyfastcte as (
select *, 
row_number() over (partition by groupingcolumn order by sortingcolumn) as rownum
from sometable
)
select *
from reallyfastcte
where rownum = 1;

您可以使用相关的子查询或使用子查询来运行与上述类似的查询,但是在几乎所有情况下,CTE都会更快。

此外,CTE确实可以帮助简化您的代码。这可能会导致性能提高,因为您更多地了解查询,并且可以引入更多的业务逻辑以帮助优化器更具选择性。

此外,如果您了解您的业务逻辑并知道应首先运行查询的哪些部分,则CTE可以提高性能 - 通常将最选择性的查询放在首先使用结果集中,该结果集可以在其下一个加入中使用索引并添加索引 option(force order) 查询提示

最后,CTE默认情况下不要使用tempdb,因此您可以通过使用它们来减少该瓶颈上的争论。

如果您需要多次查询数据,或者如果您需要查询临时表 措施 您的查询并通过插入临时表,然后添加一个索引来发现您的性能得到改善。

这里似乎对CTE有些否定性。

我对CTE的理解是,这基本上是一种Adhoc观点。 SQL既是声明性的语言,也是基于集合的语言。 CTE是宣布一套的好方法!无法为CTE索引实际上是一件好事,因为您不需要!这确实是一种句法糖,可以使查询更易于读/写。任何体面的优化器都将使用基础表上的索引来制定最佳访问计划。这意味着您可以通过遵循基础表的索引建议有效地加快CTE查询。

另外,仅仅因为您将集合定义为CTE,这并不意味着必须处理集合中的所有行。取决于查询,优化器可能会处理“足够”行以满足查询。也许您只需要前20个左右的时间。如果您构建了一个临时表,那么您确实需要读/写所有这些行!

基于此,我想说的是CTE是SQL的重要功能,可以在任何使查询更容易阅读的地方使用。我只想考虑一个批处理过程的临时表,该过程确实需要处理每个记录。即使那样,AFAIK也没有真正推荐,因为在临时表上,数据库很难帮助您进行缓存和索引。最好拥有一个带有PK字段的永久桌子,您的交易独有。

我必须承认我的经验主要是DB2,因此我假设CTE在两种产品中都以类似的方式工作。如果CTE在SQL Server中以某种状态不等,我会很高兴地纠正。 )

许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top