【问题标题】:how to optimize this one to reduce execution time如何优化这个以减少执行时间
【发布时间】:2021-01-25 07:12:44
【问题描述】:
Use ReportingDb
select hd.company_name as CompanyName,
        COALESCE((select sum(case datepart(dw, hrd.created_datetime)
             when 1 then 1 else 0
        end) from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Monday,
       COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 2 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Tuesday,
         COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 3 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Wednesday,
         COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 4 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Thursday,
         COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 5 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and(hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Friday,
         COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 6 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Saturday,
         COALESCE((select sum(case datepart(dw,hrd.created_datetime)
             when 7 then 1 else 0
        end)from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as Sunday,
        COALESCE((select COUNT_BIG(*) from dbo.HdSurvey_Result_Details hrd where hd.company_id = hrd.company_id and(hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')),0) as TotalResults
from HdSurvey_Result_Details as hd
group by hd.company_name,hd.company_id

【问题讨论】:

标签: sql sql-server optimization query-performance


【解决方案1】:

我认为最初的时间问题是查询优化器可能无法很好地优化所有子查询(例如,SELECT SUM(CASE ... 语句)的数量/方法。

如果可能,您确实需要尝试简化方法。这是可能的。理想情况下,您可以将“SELECT”组件作为简单的SUM(CASE ... 语句执行,而无需完整的子查询。这使查询优化器有机会(比如说)决定只读取表一次而不是 10 次。

首先,我总是检查分组:每一行应该是什么?在这种情况下,每一行都是一个公司名称。你设置得很好。

下一步是优化聚合组件。这是您问题中的一个示例(大多数似乎都是这样)。为了帮助我,我对其进行了一些不同的格式化。

COALESCE(
    (select  sum(case datepart(dw, hrd.created_datetime)
               when 1 then 1 else 0
               end) 
       from  dbo.HdSurvey_Result_Details hrd 
       where hd.company_id = hrd.company_id and (hrd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')
    ), 
    0) as Monday,

据我所知,这是 a) 按 hrd.created_datetime 过滤,b) 计算星期一范围内的行数。

相反,这可以通过将过滤放在 CASE 中来大大简化 - (消除对完整子查询的需要)例如,

SUM(CASE WHEN datepart(dw, hd.created_datetime) = 1 AND (hd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19') THEN 1 ELSE 0 END) AS Monday

由于它已经由公司分组,因此无需将它/等加入公司。这由底部的 GROUP BY 处理。

另外,这里是 total_results 值的一种方法

SUM(CASE WHEN (hd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19') THEN 1 ELSE 0 END) as TotalResults

如果你对所有的聚合值做这些,我相信你会得到很好的改进。

但是,我们可以走得更远。

您似乎正在按相同的日期范围过滤每个计数/总和值。无需将其包含在 SUM(CASE) 语句中,只需使用 WHERE 子句过滤原始数据,例如,

select  hd.company_name as [CompanyName],
        SUM(CASE WHEN datepart(dw, hd.created_datetime) = 1 THEN 1 ELSE 0 END) AS [Monday],
        -- add similar rows for Tuesday to Sunday
        -- Total results no longer needs the SUM(CASE) as all rows match
        COUNT_BIG(*) as [TotalResults]
from  HdSurvey_Result_Details as hd
WHERE (hd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 10:46:19')
group by hd.company_name, hd.company_id

这意味着您不必读取整个表,然后在整个表中计算每个聚合(例如,在您的 SUM 中很多行都为 0),而只能读取表的一小部分(希望)并且只对这个小得多的数据集进行聚合计算。

编辑:错别字 - 我在解决方案中留下了一些 hrd 表引用而不是 hd。这些已更改为 hd。


更新以下 cmets - 包括所有公司名称

要获取所有公司名称,如果不存在则为 0,将上面的内容用作 LEFT JOIN 的一部分(其中左表是公司名称)。

这是一个示例 - 假设公司存储在“公司”表中并具有 CTE。你也可以做一个子查询。

; WITH A AS
    (select hd.company_Id,   -- Note - changed this to company_id rather than company_name       
            SUM(CASE WHEN datepart(dw, hd.created_datetime) = 1 THEN 1 ELSE 0 END) AS [Monday],
            -- add similar rows for Tuesday to Sunday
            -- Total results no longer needs the SUM(CASE) as all rows match
            COUNT_BIG(*) as [TotalResults]
    from  HdSurvey_Result_Details as hd
    WHERE (hd.created_datetime between '2019-10-10 10:46:19' AND '2020-10-10 
    10:46:19')
    group by hd.company_id      -- Also changed this to company_id
    )
SELECT c.company_name,
       COALESCE(A.[Monday],0) AS Monday,
       --- other days
       COALESCE(A.[TotalResults],0) AS TotalResults
FROM   companies AS c
       LEFT OUTER JOIN A on c.company_id = A.company_id

【讨论】:

  • 每家公司都没有结果时我想显示0
  • 顿悟高级护理 0 0 0 0 0 0 0 0 pCare 内部 0 0 0 0 0 0 0 0 明尼苏达大学医师 0 0 0 0 0 0 0 0 V15 圣路易斯弗吉尼亚州医疗中心 0 0 0 0 0 0 0 0 V21 Palo Alto 医疗保健系统 0 0 0 0 0 0 0 0 La Rabida 儿童医院 0 0 0 0 0 0 0 0 Mayo Clinics Red Wing 0 0 0 0 0 0 0 0 Mayo Clinics SE Region 0 0 0 0 0 0 0 0 V17 北德克萨斯医疗保健系统 0 0 0 0 0 0 0 0 V23 内布拉斯加州西爱荷华州医疗保健系统 0 0 0 0 0 0 0 0 VA 大洛杉矶医疗保健系统 0 0 0 0 0 0 0 0
  • 那就不要做我们可以走得更远。接近并在每个 CASE 中保持 WHERE 条件。
  • @dnoeth 请明智地发送语法
  • 要获取所有公司名称,然后获取上述数据,从某处获取所有公司名称,然后 LEFT JOIN 到上表(以及 COALESCE 或 ISNULL 以适当获取 0)。我已经用一个例子更新了答案。
猜你喜欢
  • 2020-02-10
  • 1970-01-01
  • 2019-12-14
  • 2012-11-09
  • 2021-07-22
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
相关资源
最近更新 更多