Bla*_*man 129 sql-server performance temp-tables common-table-expression sql-server-2008
哪个更高效,CTE
或者Temporary Tables
?
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)
在上面的计划中注意,没有提到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或派生表的中间实现.但是,没有为此创建统计数据,即使假脱机行的数量与估计的数量大不相同,也不可能使进行中的执行计划动态适应响应(至少在当前版本中).自适应查询计划可能成为可能未来).
gbn*_*gbn 62
我会说他们是不同的概念,但不能说"粉笔和奶酪".
临时表适用于重用或对一组数据执行多个处理过程.
CTE可用于递归或简单地提高可读性.
并且,像视图或内联表值函数也可以像处理宏一样在主查询中进行扩展
临时表是另一个表,其中包含一些围绕范围的规则
我存储了procs,我使用它们(和表变量)
小智 47
CTE有其用途 - 当CTE中的数据很小并且与递归表中的情况一样具有强大的可读性改进.但是,它的性能肯定不比表变量好,当一个人处理非常大的表时,临时表明显优于CTE.这是因为您无法在CTE上定义索引,并且当您有大量需要与另一个表连接的数据时(CTE就像一个宏).如果要连接多个表,每个表中包含数百万行记录,则CTE的执行速度会比临时表差得多.
mar*_*c_s 32
临时表始终在磁盘上 - 因此只要您的CTE可以保存在内存中,它很可能会更快(就像表变量一样).
但话又说回来,如果你的CTE(或临时表变量)的数据负载太大,它也会存储在磁盘上,所以没有什么大的好处.
一般来说,我比临时表更喜欢CTE,因为它在我使用它之后就消失了.我不需要考虑明确地删除它或任何东西.
所以,最后没有明确的答案,但就个人而言,我更喜欢CTE而不是临时表.
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)
小智 6
我两者都用过,但在大量复杂的程序中,我总是发现临时表更适合使用且更有条理。CTE 有其用途,但通常数据较小。
例如,我创建的 sproc 会在 15 秒内返回大量计算的结果,但将此代码转换为在 CTE 中运行,并且已经看到它运行超过 8 分钟以实现相同的结果。
聚会迟到了,但是……
我的工作环境受到很大限制,需要支持一些供应商产品并提供报告等“增值”服务。由于政策和合同的限制,我通常不允许拥有单独的表/数据空间和/或创建永久代码的能力[它会变得更好一点,具体取决于应用程序]。
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(直到我获得数据空间)。
我发现 CTE 具有出色性能的一个用途是,我需要将一个相对复杂的查询连接到几个表,每个表有几百万行。
我使用 CTE 首先选择基于索引列的子集,首先将这些表削减为每个表几千个相关行,然后将 CTE 连接到我的主查询。这极大地减少了我的查询的运行时间。
虽然 CTE 的结果没有缓存,并且表变量可能是更好的选择,但我真的只是想尝试一下并发现它们适合上述场景。
因此,我分配给优化的查询是用SQL Server中的两个CTE编写的.花了28秒.
我花了两分钟将它们转换为临时表,查询耗时3秒
我在正在连接的字段上为临时表添加了一个索引,并将其降低到2秒
三分钟的工作,现在通过移除CTE,运行速度提高了12倍.我个人不会使用CTE,因为它们也更难以调试.
疯狂的事情是,CTE都只使用过一次,而且它们的指数仍被证明要快50%.