CTE
和临时表
哪个性能更高?
最佳答案
这取决于。
首先
什么是通用表表达式?
(非递归)CTE 的处理方式与其他也可用作 SQL Server 中的内联表表达式的构造非常相似。派生表、 View 和内联表值函数。请注意,虽然 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 中进行了讨论。 。然而,没有对此创建统计信息,即使假脱机行数与估计值有很大不同,正在进行的执行计划也不可能动态适应响应(至少在当前版本中)。自适应查询计划可能在未来)。
关于sql-server - CTE 和临时表哪个性能更高?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/690465/