在 SQL Server 执行计划中优化 Table Spool
Posted
技术标签:
【中文标题】在 SQL Server 执行计划中优化 Table Spool【英文标题】:optimize Table Spool in SQL Server Execution plan 【发布时间】:2017-04-21 06:13:16 【问题描述】:我有以下 sql 查询并尝试使用执行计划对其进行优化。在执行计划中,它说估计子树成本是 36.89。有几个表线轴(Eager Spool)。谁能帮我优化这个查询。提前致谢。
SELECT
COUNT(DISTINCT bp.P_ID) AS total,
COUNT(DISTINCT CASE WHEN bc.Description != 'S' THEN bp.P_ID END) AS m_count,
COUNT(DISTINCT CASE WHEN bc.Description = 'S' THEN bp.P_ID END) AS s_count,
COUNT(DISTINCT CASE WHEN bc.Description IS NULL THEN bp.P_ID END) AS n_count
FROM
progress_tbl AS progress
INNER JOIN Person_tbl AS bp ON bp.P_ID = progress.person_id
LEFT OUTER JOIN Status_tbl AS bm ON bm.MS_ID = bp.MembershipStatusID
LEFT OUTER JOIN Membership_tbl AS m ON m.M_ID = bp.CurrentMembershipID
LEFT OUTER JOIN Category_tbl AS bc ON bc.MC_ID = m.MembershipCategoryID
WHERE
logged_when BETWEEN '2017-01-01' AND '2017-01-31'
【问题讨论】:
我没有看到b
别名。我也没有看到 SELECT 部分中使用的 b_p
别名。发布的查询有问题
我在发布之前编辑了查询。我会相应地改变它
由于您现在提供了一个有效的查询,请参阅我的编辑
【参考方案1】:
这是一种你可以使用的技术。
WITH T AS
(
SELECT DISTINCT CASE
WHEN bc.Description != 'S' THEN 'M'
WHEN bc.Description = 'S' THEN 'S'
WHEN bc.Description IS NULL THEN 'N'
END AS type,
bp.P_ID
FROM progress_tbl AS progress
INNER JOIN Person_tbl AS bp
ON bp.P_ID = progress.person_id
LEFT OUTER JOIN Status_tbl AS bm
ON bm.MS_ID = bp.MembershipStatusID
LEFT OUTER JOIN Membership_tbl AS m
ON m.M_ID = bp.CurrentMembershipID
LEFT OUTER JOIN Category_tbl AS bc
ON bc.MC_ID = m.MembershipCategoryID
WHERE logged_when BETWEEN '2017-01-01' AND '2017-01-31'
)
SELECT COUNT(DISTINCT P_ID) AS total,
COUNT(CASE WHEN type= 'M' THEN P_ID END) AS m_count,
COUNT(CASE WHEN type= 'S' THEN P_ID END) AS s_count,
COUNT(CASE WHEN type= 'N' THEN P_ID END) AS n_count
FROM T
我将通过一个更简单的例子来演示它。
假设您现有的查询是
SELECT
COUNT(DISTINCT number) AS total,
COUNT(DISTINCT CASE WHEN name != 'S' THEN number END) AS m_count,
COUNT(DISTINCT CASE WHEN name = 'S' THEN number END) AS s_count,
COUNT(DISTINCT CASE WHEN name IS NULL THEN number END) AS n_count
FROM master..spt_values;
你可以改写如下
WITH T AS
(
SELECT DISTINCT CASE
WHEN name != 'S'
THEN 'M'
WHEN name = 'S'
THEN 'S'
ELSE 'N'
END AS type,
number
FROM master..spt_values
)
SELECT COUNT(DISTINCT number) AS total,
COUNT(CASE WHEN type= 'M' THEN number END) AS m_count,
COUNT(CASE WHEN type= 'S' THEN number END) AS s_count,
COUNT(CASE WHEN type= 'N' THEN number END) AS n_count
FROM T
请注意,重写的成本要低得多,而且计划要简单得多。
【讨论】:
感谢您的回复。试过你的,它将子树成本从 36.98 降低到 26.87。您认为我们无法进一步优化还是因为数据无法进一步降低子树成本? @KapilaPerera - 不能说,因为它取决于连接的执行计划和WHERE
,而您显示的计划不包括这些。无论如何,那将是一个不同的问题。你在这里问的问题是关于摆脱线轴的。【参考方案2】:
如前所述,您的查询似乎存在一些拼写错误/复制粘贴问题。这让我们很难弄清楚发生了什么。
table-spools 可能是 CASE WHEN b.description etc...
构造中发生的事情。 MSSQL 首先创建一个包含所有结果值的(内存)表,然后通过COUNT(DISTINCT ...)
运算符对该表进行排序和流式传输。我认为您对此无能为力,因为工作需要在某个地方完成。
总之,一些言论和胡思乱想:
我猜logged_when
在progress_tbl
表中?
如果是这样,您真的需要 LEFT OUTER JOIN 所有其他表吗?据我所知,它们没有被使用?
您正在尝试计算与条件匹配的P_ID
s 的数量,并且您想将该数量分配给b.Description
为“S”、其他内容或NULL 的那些。
为此,您可以将总数计算为 m_count、s_count 和 n_count 的总和。这将为您节省 1 个 COUNT()
操作,不确定它在更大的范围内是否有很大帮助,但我猜所有位都有帮助。
类似这样的:
;WITH counts AS (
SELECT
COUNT(DISTINCT CASE WHEN b.Description != 'S' THEN b_p.P_ID END) AS m_count,
COUNT(DISTINCT CASE WHEN b.Description = 'S' THEN b_p.P_ID END) AS s_count,
COUNT(DISTINCT CASE WHEN b.Description IS NULL THEN b_p.P_ID END) AS n_count
FROM
progress_tbl AS progress
INNER JOIN Person_tbl AS bp ON bp.P_ID = progress.person_id
LEFT OUTER JOIN Status_tbl AS bm ON bm.MS_ID = bp.MembershipStatusID -- really needed?
LEFT OUTER JOIN Membership_tbl AS m ON m.M_ID = bp.CurrentMembershipID -- really needed?
LEFT OUTER JOIN Category_tbl AS bc ON bc.MC_ID = m.MembershipCategoryID -- really needed?
WHERE
logged_when BETWEEN '2017-01-01' AND '2017-01-31' -- what table does logged_when column come from????
)
SELECT total = m_count + s_count + n_count,
*
FROM counts
更新
注意:使用 Martin Smith 的答案/示例代码,我意识到total
不一定是其他字段的总和。可能是给定的P_ID
显示为不同的description
,然后可能属于不同的类别。因此,根据您的数据,我的回答可能是完全错误的。
【讨论】:
进度表有记录的列时 bc.Description 在选择查询中使用,所以我需要使用 Category_tbl。要访问 Category_tbl,我需要其余的表才能加入。 我使用 LEFT OUTER JOIN 来获取 bc 中不匹配的行。Description == null 来获取非成员以上是关于在 SQL Server 执行计划中优化 Table Spool的主要内容,如果未能解决你的问题,请参考以下文章