与临时表相比,为什么 CTE 这么慢?
Why CTE is so slow comparing to Temp tables?
我有一个简单的 stored procedure 我有多个 WITH
clauses.
一些代码是这样的:
WITH cteRowNums AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY fcmp.EmpUserID, fcmp.WorkCellID, fcmp.ActivityTS) AS RowNumber,
fcmp.ActivityTS,
fcmp.ArtifactTypeID,
fcmp.ServerDateID,
fcmp.ServerHourID,
fcmp.EmpUserID,
fcmp.WorkCellID
FROM dbo.FactCassetteMarkingProcessing fcmp
WHERE ServerDateID >= '2007-01-01'
),
-- Make an attempt at identifying what each user did in their "session" by self-joining
cteJoinCurAndNext AS
(
SELECT
[Current Row].ArtifactTypeID,
[Current Row].ServerDateID,
[Current Row].ServerHourID,
[Current Row].EmpUserID,
[Current Row].WorkCellID
FROM cteRowNums [Current Row]
LEFT OUTER JOIN cteRowNums [Next Row] ON [Next Row].RowNumber = [Current Row].RowNumber + 1
WHERE [Current Row].ArtifactTypeID = 2
OR ([Current Row].ArtifactTypeID = 1 AND [Next Row].ArtifactTypeID = 2
AND [Current Row].EmpUserID = [Next Row].EmpUserID
AND [Current Row].WorkCellID = [Next Row].WorkCellID)
),
-- Do some aggregations
cteAggregates AS
(
SELECT
EmpUserID,
ServerDateID,
ServerHourID,
COUNT(NULLIF(ArtifactTypeID, 2)) AS SpecimensProcessedCount,
COUNT(NULLIF(ArtifactTypeID, 1)) AS BlocksProcessedCount
FROM cteJoinCurAndNext
GROUP BY EmpUserID, ServerDateID, ServerHourID
)
SELECT * FROM cteAggregates
问题是这需要花费大量时间来处理大约 250 万行。我在40分钟取消了执行查询
如果我将这段代码更改为 temporary
table,执行速度会快很多。有什么方法可以仅使用 CTEs
获得几乎相同的 性能 吗?
有两个原因。
可能更重要的原因是SQL服务器没有具体化CTE。因此,对于每个引用,SQL 服务器都会重新计算整个 CTE。据我所知,SQL Server 也不对执行 DAG 做常见的子查询优化,所以它总是重新生成 CTES(尽管每个实例的执行计划可能不同)。
第二个原因是临时表有统计信息,这些统计信息可以告知查询计划以创建更好的计划。
我怀疑你可以简化逻辑。但是,您需要提出一个 new 问题,并解释您想要做什么,以及样本数据和期望的结果。
我有一个简单的 stored procedure 我有多个 WITH
clauses.
一些代码是这样的:
WITH cteRowNums AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY fcmp.EmpUserID, fcmp.WorkCellID, fcmp.ActivityTS) AS RowNumber,
fcmp.ActivityTS,
fcmp.ArtifactTypeID,
fcmp.ServerDateID,
fcmp.ServerHourID,
fcmp.EmpUserID,
fcmp.WorkCellID
FROM dbo.FactCassetteMarkingProcessing fcmp
WHERE ServerDateID >= '2007-01-01'
),
-- Make an attempt at identifying what each user did in their "session" by self-joining
cteJoinCurAndNext AS
(
SELECT
[Current Row].ArtifactTypeID,
[Current Row].ServerDateID,
[Current Row].ServerHourID,
[Current Row].EmpUserID,
[Current Row].WorkCellID
FROM cteRowNums [Current Row]
LEFT OUTER JOIN cteRowNums [Next Row] ON [Next Row].RowNumber = [Current Row].RowNumber + 1
WHERE [Current Row].ArtifactTypeID = 2
OR ([Current Row].ArtifactTypeID = 1 AND [Next Row].ArtifactTypeID = 2
AND [Current Row].EmpUserID = [Next Row].EmpUserID
AND [Current Row].WorkCellID = [Next Row].WorkCellID)
),
-- Do some aggregations
cteAggregates AS
(
SELECT
EmpUserID,
ServerDateID,
ServerHourID,
COUNT(NULLIF(ArtifactTypeID, 2)) AS SpecimensProcessedCount,
COUNT(NULLIF(ArtifactTypeID, 1)) AS BlocksProcessedCount
FROM cteJoinCurAndNext
GROUP BY EmpUserID, ServerDateID, ServerHourID
)
SELECT * FROM cteAggregates
问题是这需要花费大量时间来处理大约 250 万行。我在40分钟取消了执行查询
如果我将这段代码更改为 temporary
table,执行速度会快很多。有什么方法可以仅使用 CTEs
获得几乎相同的 性能 吗?
有两个原因。
可能更重要的原因是SQL服务器没有具体化CTE。因此,对于每个引用,SQL 服务器都会重新计算整个 CTE。据我所知,SQL Server 也不对执行 DAG 做常见的子查询优化,所以它总是重新生成 CTES(尽管每个实例的执行计划可能不同)。
第二个原因是临时表有统计信息,这些统计信息可以告知查询计划以创建更好的计划。
我怀疑你可以简化逻辑。但是,您需要提出一个 new 问题,并解释您想要做什么,以及样本数据和期望的结果。