哪些效果更高,CTE
或Temporary Tables
?
答案 0 :(得分:157)
取决于。
首先
什么是通用表格表达式?
(非递归)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;
示例1
WITH CTE1 AS
(
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780
在上面的计划中注意,没有提到CTE1。它只是直接访问基表,并被视为与
相同SELECT A,
ABS(B) AS Abs_B,
F
FROM T
WHERE A = 780
通过将CTE具体化为中间临时表来进行重写将大大适得其反。
实现
的CTE定义SELECT A,
ABS(B) AS Abs_B,
F
FROM T
将涉及将大约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
以上示例在我的机器上大约需要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
将查询的一部分中间实现到临时表中有时可能很有用,即使它只被评估一次 - 当它允许使用有关物化结果的统计信息重新编译其余查询时。这种方法的一个示例在SQL Cat文章When To Break Down Complex Queries中。
在某些情况下,SQL Server将使用假脱机来缓存中间结果,例如CTE,并避免重新评估该子树。这在(迁移的)连接项Provide a hint to force intermediate materialization of CTEs or derived tables中进行了讨论。但是,没有为此创建统计数据,即使假脱机行的数量与估计的数量大不相同,也不可能使进行中的执行计划动态适应响应(至少在当前版本中)。自适应查询计划可能成为可能未来)。
答案 1 :(得分:61)
我会说他们是不同的概念,但不能说“粉笔和奶酪”。
临时表适合重复使用或对一组数据执行多次处理。
CTE既可用于递归,也可用于简单提高可读性。
并且,像视图或内联表值函数也可以被视为要在主查询中展开的宏
临时表是另一个包含范围规则的表
我存储了procs,我同时使用它们(和表变量)
答案 2 :(得分:47)
CTE有其用途 - 当CTE中的数据很小并且与递归表中的情况一样具有强大的可读性改进。但是,它的性能肯定不比表变量好,当一个人处理非常大的表时,临时表明显优于CTE。这是因为您无法在CTE上定义索引,并且当您有大量需要与另一个表连接的数据时(CTE就像一个宏)。如果要连接多个表,每个表中包含数百万行记录,则CTE的执行速度会比临时表差得多。
答案 3 :(得分:32)
临时表始终在磁盘上 - 因此只要您的CTE可以保存在内存中,它很可能会更快(就像表变量一样)。
但话又说回来,如果你的CTE(或临时表变量)的数据负载太大,它也会存储在磁盘上,所以没有什么大的好处。
一般来说,我更喜欢临时表上的CTE,因为它在我使用它之后就消失了。我不需要考虑明确地删除它或任何东西。
所以,最后没有明确的答案,但就个人而言,我更喜欢CTE而不是临时表。
答案 4 :(得分:6)
CTE不会占用任何物理空间。它只是我们可以使用join的结果集。
临时表是临时的。我们可以创建索引,约束就像普通表一样,我们需要定义所有变量。
临时表的范围仅在会话中。 EX: 打开两个SQL查询窗口
create table #temp(empid int,empname varchar)
insert into #temp
select 101,'xxx'
select * from #temp
在第一个窗口中运行此查询 然后在第二个窗口中运行以下查询,您可以找到差异。
select * from #temp
答案 5 :(得分:5)
因此我分配给优化的查询是用SQL Server中的两个CTE编写的。花了28秒。
我花了两分钟将它们转换为临时表,查询耗时3秒
我在正在加入的字段上为临时表添加了一个索引,并将其降低到2秒
三分钟的工作,现在通过移除CTE,它的运行速度提高了12倍。我个人不会使用CTE,他们也很难调试。
疯狂的事情是CTE都只使用过一次,而且它们的指数仍被证明要快50%。
答案 6 :(得分:4)
我已经使用了两者,但是在大量复杂的程序中总是发现临时表更好地使用并且更有条理。 CTE有其用途,但通常数据量很小。
例如,我已经创建了在15秒内返回大量计算结果的sprocs,但是将此代码转换为在CTE中运行,并且已经看到它运行超过8分钟以获得相同的结果。
答案 7 :(得分:3)
晚会,但......
我工作的环境受到严格限制,支持某些供应商产品并提供报告等“增值”服务。由于政策和合同的限制,我通常不允许奢侈的单独的表/数据空间和/或创建永久代码的能力[它会变得更好,具体取决于应用程序]。
IOW,我不能通常开发存储过程或UDF或临时表等。我几乎必须通过MY应用程序界面做任何事情(Crystal Reports - 添加/链接表,设置来自CR等的条款等)。一个SMALL保存优雅是Crystal允许我使用COMMANDS(以及SQL表达式)。通过定义添加/链接表功能无效的一些事情可以通过定义SQL命令来完成。我通过它使用CTE并且“远程”获得了非常好的结果。 CTE还可以帮助报告维护,不需要开发代码,交给DBA进行编译,加密,传输,安装,然后需要进行多级测试。我可以通过本地界面进行CTE。
使用带CR的CTE的不利方面是,每个报告都是独立的。必须为每份报告维护每个CTE。在我可以做SP和UDF的地方,我可以开发一些可以被多个报告使用的东西,只需要链接到SP并传递参数,就好像你在常规表上工作一样。 CR并不擅长将参数处理成SQL命令,因此可能缺乏CR / CTE方面的方面。在这些情况下,我通常会尝试定义CTE以返回足够的数据(但不是所有数据),然后使用CR中的记录选择功能对切片进行切片和切块。
所以...我的投票是针对CTE的(直到我得到我的数据空间)。
答案 8 :(得分:3)
我发现CTE表现优异的一个用途是我需要将一个相对复杂的查询加入到几个每行有几百万行的表中。
我使用CTE首先选择基于索引列的子集,首先将这些表分别缩减到几千个相关行,然后将CTE加入到我的主查询中。这以指数方式减少了查询的运行时间。
虽然没有缓存CTE的结果,而且表变量可能是更好的选择,但我真的只是想尝试一下,发现它符合上述情况。
答案 9 :(得分:1)
这是一个非常开放的问题,这完全取决于它的使用方式和临时表的类型(表变量或传统表)。
传统的临时表将数据存储在临时数据库中,这会降低临时表的速度;但表变量没有。
答案 10 :(得分:1)
我刚测试了这个 - CTE和非CTE(每个联合实例都输出了查询)都花了大约31秒。 CTE使代码更具可读性 - 从241行减少到130行非常好。另一方面,临时表将其减少到132行,并运行五个秒。可不是闹着玩的。所有这些测试都被缓存了 - 查询之前都运行了多次。
答案 11 :(得分:1)
根据我在SQL Server中的经验,我发现其中一个场景中CTE优于Temp表
我需要在我的存储过程中使用复杂查询中的DataSet(~100000)。
临时表导致我的程序所在的SQL开销 缓慢执行(因为临时表是真实的物化表 存在于tempdb中并且在我当前程序的生命周期中持续存在)
另一方面,对于CTE,CTE仅持续到以下为止 查询运行。因此,CTE是一种有限的内存结构 范围。 CTE默认不使用tempdb。
这是CTE可以真正帮助简化代码和优于Temp Table的一种情况。 我曾经使用过2个CTE,比如
WITH CTE1(ID, Name, Display)
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO