在SQL Server执行计划中优化表假脱机

时间:2017-04-21 06:13:16

标签: sql sql-server query-optimization

我有以下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'

enter image description here

2 个答案:

答案 0 :(得分:2)

这是您可以使用的技术。

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

请注意,重写的成本要低得多,而且计划要简单得多。

enter image description here

答案 1 :(得分:0)

正如已经指出的那样,您的查询似乎存在一些拼写错误/复制粘贴问题。这使我们很难弄清楚发生了什么。

表格线轴可能是CASE WHEN b.description etc...结构中正在进行的操作。 MSSQL首先创建一个包含所有结果值的(内存)表,然后通过COUNT(DISTINCT ...)运算符对其进行排序和流式处理。我不认为你可以做很多事情,因为工作需要在某个地方完成。

无论如何,一些言论和猜测:

  • 我猜测logged_whenprogress_tbl表中?
  • 如果是这样,你真的需要LEFT OUTER JOIN所有其他表吗?据我所知,他们没有被使用?
  • 您正在尝试计算符合条件的P_ID的数量,并且您希望在具有b.Description的情况下将该数字拆分为S' 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,然后可能会分为不同的类别。因此,根据您的数据,我的答案可能是完全错误的。