【问题标题】:Query including subquery and group by slower than expected包括子查询和分组的查询比预期的要慢
【发布时间】:2015-07-26 00:37:38
【问题描述】:

下面的整个查询运行速度非常慢。

子查询[alias Stage_1]只用了1.37分钟,返回9514条记录,但整个查询耗时20多分钟,返回2606条记录。

我可以使用 #temp 表来保存子查询以提高性能,但我不希望这样做。

查询的概述是表 WeeklySpace 内部连接到 SpaceblockName_SID 上的 Spaceblock_Name_to_PG 表,这会减少 WeeklySpace 中的结果并将 PG_Code 包含在 WeeklySpace 中的结果中。然后 WeeklySpace 在 3 个字段中与 Sales_PG_Wk 完全外联。 where 子句侧重于结果,并且可以更改。然后对子查询的结果求和。由于 group by 和 sum 过度使用,您无法在子查询中进行最终求和。

我认为问题是由于在最终求和中分组期间反复重新计算子查询。字段 SpaceblockName_SID 似乎也与导致问题有关,因为没有它,子查询中的 group by 的运行时间不会受到影响。

我已经阅读了大量建议,并尝试全部解决问题。

这些包括;

  • 添加 TOP 2147483647 和 Order by 以强制中间 在子查询中和使用 CTE 实现具体化。
  • 在 stage_1 之后添加连接。
  • 将 SpaceblockName_SID 从 int 转换为 varchar 并再次返回

子查询和整个查询的执行计划(分为两部分,显示在代码下方)看起来相似。成本大约是我预期的完全外连接(哈希匹配)。

查询在 T-SQL 2005 上运行。

非常感谢任何帮助!

select 
    Cost_centre
    , Fin_week
    , SpaceblockName_SID
    , sum(Propor_rep_SRV) as Total_SpaceblockName_SID_SRV
from
(
        select  
            coalesce(space_side.fin_week , sales_side.fin_week) as Fin_week 
           ,coalesce(space_side.cost_centre , sales_side.cost_Centre) as Cost_centre 
           ,space_side.SpaceblockName_SID
           ,case
                when  space_side.SpaceblockName_SID is null
                    then sales_side.SalesExVAT
                else  sum(space_side.TLM)
                                /nullif(sum (sum(space_side.TLM) ) over (partition by coalesce(space_side.fin_week , sales_side.fin_week)
                                                                                            , coalesce(space_side.cost_centre , sales_side.cost_Centre)  
                                                                                            , coalesce( Spaceblock_Name_to_PG.PG_Code, sales_side.PG_Code)) ,0)*sales_side.SalesExVAT 
            end as Propor_rep_SRV
        from
            WeeklySpace as space_side
        INNER JOIN
            Spaceblock_Name_to_PG 
                ON space_side.SpaceblockName_SID = Spaceblock_Name_to_PG.SpaceblockName_SID 
                    and Spaceblock_Name_to_PG.PG_Code < 10000
        full outer join
            sales_pg_wk as sales_side
                on  space_side.fin_week = sales_side.fin_week 
                    and space_side.Cost_Centre = sales_side.Cost_Centre 
                    and Spaceblock_Name_to_PG.PG_code = sales_side.pg_code
        where 
            coalesce(space_side.fin_week, sales_side.fin_week) between 201538 and 201550
                and
            coalesce(space_side.cost_centre, sales_side.cost_Centre) in (3, 2800)
        group by  
            coalesce(space_side.fin_week, sales_side.fin_week)  
           ,coalesce(space_side.cost_centre, sales_side.cost_Centre)  
           ,coalesce( Spaceblock_Name_to_PG.PG_Code, sales_side.PG_Code)  
           ,sales_side.SalesExVAT
           ,space_side.SpaceblockName_SID
) as stage_1
group by 
    Cost_centre
    , Fin_week
    , SpaceblockName_SID

执行计划左侧


执行计划右侧

【问题讨论】:

  • 您说您可以解决问题,但使用临时表,但不想这样做。我能问为什么吗?除非您需要将查询设为视图,否则这似乎是一个可行的选择。我花了比我愿意承认的时间更长的时间来试图解决我需要强制实现的问题并且在几乎所有情况下都承认失败,要么决定我将使用多步表值函数并使用表变量来强制实现或者我将使用存储过程和临时表。
  • @GarethD 。感谢您花时间查看它。原来不使用临时表的原因正是你说的,所以我可以看看。第二个也是主要的原因是该代码与用于许多不同个人的报告系统一起使用。拥有临时表会给报告系统带来问题。
  • '拥有一个临时表会给报告系统带来问题。' - 如何?
  • 我认为它的长处和短处是,除非this connect item 有任何结果,否则不会有一种有保证的方法来实现这一目标。像您提到的那样,有多种解决方法,您可以尝试各种提示来尝试强制急切假脱机超过惰性假脱机,但这些会根据您的表结构和统计数据而有所不同。
  • @马克辛金森。感谢您查看它。报告通过 Excel 使用 ADODB.Connection 运行。由于连接的工作方式,临时表必须是## 而不是#。因此,对于不同的用户,您必须为每个表设置不同的名称。我们已经对其他报告进行了此操作,但我们使用了注入代码,这并不理想。

标签: sql sql-server tsql sql-server-2005 subquery


【解决方案1】:

您没有提到在查询中使用的那些列上是否创建了索引。如果没有,则创建并检查查询的性能

【讨论】:

  • 感谢收看。表已编入索引,您可以在执行计划中看到这一点。正在使用的索引是唯一的非集群。由于子查询运行正常,我认为这不是索引问题。
【解决方案2】:

在查看您的逻辑时,我认为您使用 UNION 将其一分为二
一个 Spaceblock_Name_to_PG.PG_Code = 10000

考虑一下这个变化
如果可能正在做一堆你无论如何都会扔掉的连接

full outer join sales_pg_wk as sales_side
  on  space_side.fin_week = sales_side.fin_week 
      and space_side.Cost_Centre = sales_side.Cost_Centre 
      and Spaceblock_Name_to_PG.PG_code = sales_side.pg_code
      and space_side.fin_week between 201538 and 201550 
      and sales_side.fin_week between 201538 and 201550
      and space_side.cost_centre in (3, 2800)
      and sales_side.cost_Centre in (3, 2800)

【讨论】:

  • @Blam。你能再解释一下“工会”吗?抱歉,我不明白它有什么帮助,因为不需要 PG_Code >= 10000 的记录,因此通过连接删除(如果有帮助,Sales_side 中不存在 PG_code >= 10000。)将连接条件添加到完整的外部。它会减慢速度。正如我所提到的,子查询运行良好,它将最终聚合与组添加到该组中不起作用。感谢收看。
  • 那么我不遵循这个“当 space_side.SpaceblockName_SID 为空时”。因此该子查询单独运行良好。很可能你有一个案例运行了很多次。在#temp 中实现子查询有什么大不了的?这将是一个已经有 40 行长的查询的全部 4 行代码。
  • @Blam。谢谢你回到我身边。 “当 space_side.SpaceblockName_SID 为 null 时”涵盖了当没有 TLM 时,销售仍然存在,因此归因于 null 空间,否则通过将其乘以 null 丢失。使用临时表的问题在问题下向 GarethD 描述了 cmets,但是查看反馈我开始认为它是唯一和最好的选择。
  • 当 space_side.SpaceblockName_SID 为空时,联合可以覆盖。一个选择处理空值,另一个不处理空值。
  • @Blam。我试过用联合把它分成两部分。它的运行速度比以前慢,但也许我误解了你的答案。感谢您的建议。
猜你喜欢
  • 1970-01-01
  • 2015-03-25
  • 2015-02-21
  • 2018-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-21
相关资源
最近更新 更多