公用表表达式(CTE)和临时表之间有什么区别?什么时候应该在另一个上使用呢? >

评论

另请参见SQL Server中的临时表和表变量有什么区别?

与SO相关:哪个是性能更高的CTE或临时表?

#1 楼

这是相当广泛的,但是我会给您一个一般的答案。

CTEs ...


无法索引(但可以使用现有的引用对象上的索引)
不能有约束
基本上是可抛弃的VIEW s
仅持续到下一个查询运行之前
可以递归
没有专用统计信息(依赖于底层对象的统计信息)

#Temp表...


tempdb中是否存在真实的物化表
可以建立索引
可能有约束条件
在当前CONNECTION的生命周期中一直存在
可以由其他查询或子过程引用
具有引擎生成的专用统计信息

至于何时使用它们,它们有非常不同的用例。如果结果集很大,或者需要多次引用它,请将其放在#temp表中。如果需要递归,可抛弃或只是为了逻辑上的简化,则首选CTE

此外,永远不要使用CTE来提高性能。您几乎永远不会通过使用CTE来加快处理速度,因为再次,这只是一个一次性视图。您可以对它们进行一些整洁的操作,但是加快查询的速度并不是真正的其中之一。

评论


使用CTE加速大型合并是一件事情

–AgentFire
18年5月6日在18:48



使用CTE加速许多查询也是一件事情,因为使用CTE,您可以添加自己的业务知识,从而胜过查询优化器。例如,您可以从表中选择CTE的第1部分,在这些表中您知道结果行将非常小。在同一个查询中,您可以将此微小的结果集加入一些更大的结果集,并完全绕开由过时的统计信息等引起的问题。为此,您需要添加查询提示以强制执行该顺序。它可以工作,可以提高性能。

–戴夫·希尔迪奇(Dave Hilditch)
18年5月19日在13:28

尽管我理解您的观点,但“绝不用于表现”是一个广泛而有点主观的陈述。尽管除了其他注释之外,当从另一种形式的递归(例如递归过程调用或游标)切换到递归CTE时,使用CTE可能会带来其他潜在的性能提升。

– J.D.
19-3-12在3:28



#2 楼

编辑:
请在下面查看Martin的评论:

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创建表用于内存,但仅对它后面的特定查询有效。使用递归时,这可能是一种有效的结构。
您可能还需要考虑使用表变量。这用作临时表,可以使用多次,而无需为每个联接重新实现。另外,如果您现在需要保留一些记录,则在下一个选择之后添加更多记录,在另一个操作之后添加更多记录,然后仅返回少量记录,那么这可以是一个方便的结构,因为它不会执行后无需删除。通常只是语法糖。但是,如果将行数保持在较低水平,则它永远不会在磁盘上实现。请参见SQL Server中的临时表和表变量有什么区别?有关更多详细信息。
临时表
有关MSDN的更多信息-向下滚动约40%的方式临时表有两种:本地表和全局表。对于MS Sql Server,您将#tableName指定为本地,将##tableName指定为全局(请注意,使用单号或双号作为标识特征)。
请注意,与临时表相比,与表变量或CTE相对,您可以应用索引等,因为按照通常的含义,这些表是合法的表。

通常,我将临时表用于较长或较大的查询,以及CTE或表变量(如果我已经有一个小的数据集,并且想要快速编写一些小的代码的脚本)。经验和其他人的建议表明,如果从中返回少量行,则应使用CTE。如果数量很多,您可能会受益于在临时表上建立索引的功能。

评论


CTE未在内存中实现为表格。这只是封装查询定义的一种方法。对于OP,它将被内联,并且与从SomeTable进行SELECT Column1,Column2,Column3相同。

–马丁·史密斯
2012年2月15日在16:55



大多数情况下,它们不会在前面实现,这就是为什么它不使用T(X)AS返回任何行的原因(SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X = T2.X,还要检查执行计划。尽管有时可以破解该计划以获取假脱机。有一个连接项要求对此进行提示。

–马丁·史密斯
2012-02-15 17:08



#3 楼

此处公认的答案是“绝不应该使用CTE来提高性能”-但这可能会误导。在CTE与临时表的上下文中,我刚刚从一组存储的proc中删除了一大堆垃圾,因为有些痴迷一定认为使用临时表几乎没有开销。我将大量资金投入CTE,但那些在整个流程中可以合理使用的除外。从所有指标来看,我获得了大约20%的性能。然后,我开始着手删除所有试图实现递归处理的游标。这是我收获最大的地方。我最终将响应时间减少了十倍。

CTE和临时表确实有非常不同的用例。我只是想强调一点,尽管不是万能药,但CTE的理解和正确使用可以在代码质量/可维护性和速度方面带来真正真正的进步。自从有了它们的句柄以来,我将临时表和游标视为SQL处理的最大弊端。现在几乎可以使用表变量和CTE了。我的代码更干净,更快。

评论


现在,公平地说-游标是大恶魔;临时表在最坏的情况下要小一些。 :-)就像您自己看到的那样,将它们置于同一级别是不公平的。

–RDFozz
18年1月22日在20:50

@RDFozz对,众所周知,地狱有9个圈子。让我们将临时表放在第二位,将游标放在...第七位? ;)

–超立方体ᵀᴹ
18年1月22日在21:52



您知道编程中的“大恶魔”是什么吗?当人们说一种特定的技术是邪恶的。有一个游标的地方。在某些情况下,它们可以胜过其他技术。这里没有邪恶-您需要学习使用正确的工具来完成工作。衡量自己在做什么,不要相信CTE,临时表或游标是邪恶的炒作。量度-因为事实取决于情况。

–戴夫·希尔迪奇(Dave Hilditch)
18年5月19日在13:34



@DaveHilditch是一个公平的评论,但是断言在很多情况下游标不是正确的解决方案也是一个公平的评论,因此,将游标作为最后的选择是一种可行的概括。

–梅尔·帕登(Mel Padden)
18年5月22日在9:34

以我的经验,游标本身并不坏。 CURSORS通常被开发人员“错误地”使用,因为在大多数编程语言中,您必须反复考虑,而不是在SQL中,您通常必须分批考虑。我知道这是我工作场所中的一个常见错误,开发人员只是用CURSOR不能“看到”问题的出路,因此,为什么一个好的DBA可以派上用场了。 @DaveHilditch完全正确:完成所需工作的正确工具。

–菲利普
18年7月31日在17:54



#4 楼

CTE可能会在查询中重复调用,并且每次引用CTE都会对其进行评估-此过程可以递归进行。如果仅引用一次,则它的行为很像子查询,尽管可以对CTE进行参数化。

物理上临时表是临时表,可以对其进行索引。实际上,查询优化器还可以在后台(例如在后台打印操作中)持久存储中间联接或子查询的结果,因此严格来讲,CTE的结果永远不会持久存储到磁盘并不是完全正确的。

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

评论


CTE是否可以参数化?怎么样?而且,表变量并不总是内存结构。请参阅马丁对相关问题的出色回答。

–保罗·怀特♦
16-09-30在6:14

#5 楼

临时表是tempdb中的真实对象,但cte只是一种复杂查询的包装程序,可以一步一步简化组织递归的语法。

#6 楼

使用CTE的主要原因是要访问诸如row_number()之类的Window函数。

这意味着您可以执行以下操作,例如快速,高效地获取每个组的第一行或最后一行-比在大多数实际情况下,还可以采用其他方法。在几乎所有情况下。

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

此外,如果您了解业务逻辑并知道哪些部分,CTE可以提高性能。的查询应首先运行-通常,将最有选择性的查询放在第一位,从而导致可以在下一个联接中使用索引的结果集,并添加查询提示。默认情况下使用tempdb,以便通过使用它们减少瓶颈上的争用。

如果需要多次查询数据,或者如果您测量查询并通过插入来发现查询,则应使用临时表到临时表中,然后添加一个可以提高性能的索引。

评论


所有优点... +1

–梅尔·帕登(Mel Padden)
18年1月30日在13:05

#7 楼

对CTE似乎有些消极。

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

此外,仅因为您将一个集合定义为CTE,并不意味着设置必须被处理。根据查询,优化器可能会处理“足够多”的行以满足查询。也许您只需要前20个屏幕即可。如果您构建了一个临时表,那么您确实需要读取/写入所有这些行!

基于此,我想说CTE是SQL的一个很棒的功能,可以在进行查询的任何地方使用更容易阅读。我只会考虑批处理的临时表,该表确实需要处理每条记录。即便如此,也并不推荐使用afaik,因为在临时表上,数据库很难帮助您进行缓存和索引。最好有一个永久表,该表带有一个事务唯一的PK字段。

我不得不承认我的经验主要是在DB2上,所以我假设两种产品中CTE的工作方式都相似。如果CTE在SQL Server中不如亚特兰大,我会很乐意纠正。 ;)