链式 CTE 与 Temp 的性能影响 table

Performance impact of chained CTE vs Temp table

我有以下链式 CTE 查询(简化):

;WITH CTE1
AS(    
    SELECT * FROM TableA
),
CTE2
AS(
    SELECT * FROM TableB b INNER JOIN CTE1 c ON b.id = c.id
)

SELECT * FROM CTE2

如果我打破 CTE 链并将 CTE1 的数据存储到一个临时文件中 table,那么整体查询的性能会提高(从 1 分 20 秒到 8 秒)。

;WITH CTE1
AS(    
    SELECT * FROM TableA
)

SELECT * INTO #Temp FROM CTE1

;WITH CTE2
AS(
    SELECT * FROM TableB b INNER JOIN #Temp c ON b.id = c.id
)

SELECT * FROM CTE2
DROP TABLE #Temp

CTE1 和 CTE2 中有复杂的查询。我刚刚创建了一个简化版本来在这里进行解释。

打破 CTE 椅子应该提高性能吗?

SQL服务器版本:2008 R2

显然,它可以,正如您自己所展示的那样。

为什么?最明显的原因是优化器知道临时 table 的大小。这为它提供了更多用于优化查询的信息。 CTE 只是一个估计值。所以,您看到的改进是由于查询计划。

另一个原因是 CTE 在查询中被多次引用。 SQL 服务器没有具体化 CTE,因此定义代码将 运行 多次。

有时,您故意将 CTE 实现为临时 table,以便您可以向它们添加索引。这也可以提高性能。

综上所述,我更愿意避免临时 tables。优化器通常很好。

考虑到 cte1 很昂贵

;WITH CTE1
AS(    
    SELECT * FROM TableA
)

SELECT * INTO #Temp FROM CTE1

以上保证cte1只有运行一次。

链式 cte 可以多次计算 cte1。

即使使用#temp,您也应该考虑索引/PK 并对插入进行排序。

这取决于很多因素。如果可以,请始终尝试编写单个语句。过早的优化是许多罪恶的根源。

如果您确实遇到性能问题,分解单个语句的一些优势如下:

  • 它可以通过降低复杂性来提高可维护性,这是许多非功能性需求之一。
  • 只要中间实体化的成本和节省的时间低于原来的成本,就能得到更好的方案。
  • 中间表可以被索引。
  • 索引、主键和唯一约束对优化器非常有帮助,不仅可以选择连接类型,还可以估计基数,这对内存授予有很大影响。
  • 您可以选择将优化器提示(例如 MAXDOP)仅应用于 select 语句,而不是一个巨大的语句。这在您需要操纵内存授予时特别有用。
  • 您可以调整单个语句以消除溢出到 tempdb。
  • 根据进程的复杂性和总执行时间,您可以提前释放资源锁,这也取决于您的语句 运行 所处的隔离级别。
  • 如果您的查询计划很差,由于优化器超时,使用不太复杂的单个语句可能会产生更好的整体结果。