链式 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。
- 根据进程的复杂性和总执行时间,您可以提前释放资源锁,这也取决于您的语句 运行 所处的隔离级别。
- 如果您的查询计划很差,由于优化器超时,使用不太复杂的单个语句可能会产生更好的整体结果。
我有以下链式 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。
- 根据进程的复杂性和总执行时间,您可以提前释放资源锁,这也取决于您的语句 运行 所处的隔离级别。
- 如果您的查询计划很差,由于优化器超时,使用不太复杂的单个语句可能会产生更好的整体结果。