为几个分层组优化 SUM OVER PARTITION BY

Posted

技术标签:

【中文标题】为几个分层组优化 SUM OVER PARTITION BY【英文标题】:Optimizing SUM OVER PARTITION BY for several hierarchical groups 【发布时间】:2018-05-24 19:29:11 【问题描述】:

我有一张如下表:

Region    Country    Manufacturer    Brand    Period    Spend
R1        C1         M1              B1       2016      5
R1        C1         M1              B1       2017      10
R1        C1         M1              B1       2017      20
R1        C1         M1              B2       2016      15
R1        C1         M1              B3       2017      20
R1        C2         M1              B1       2017      5
R1        C2         M2              B4       2017      25
R1        C2         M2              B5       2017      30
R2        C3         M1              B1       2017      35
R2        C3         M2              B4       2017      40
R2        C3         M2              B5       2017      45

我需要在不同的组中找到SUM([Spend],如下所示:

    整个表中所有行的总支出 每个区域的总支出 每个地区和国家组的总支出 每个地区、国家和广告客户组的总支出

所以我在下面写了这个查询:

SELECT 
    [Period]
    ,[Region]
    ,[Country]
    ,[Manufacturer]
    ,[Brand]
    ,SUM([Spend]) OVER (PARTITION BY [Period]) AS [SumOfSpendWorld]
    ,SUM([Spend]) OVER (PARTITION BY [Period], [Region]) AS [SumOfSpendRegion]
    ,SUM([Spend]) OVER (PARTITION BY [Period], [Region], [Country]) AS [SumOfSpendCountry]
    ,SUM([Spend]) OVER (PARTITION BY [Period], [Region], [Country], [Manufacturer]) AS [SumOfSpendManufacturer]
FROM myTable

但是对于只有 450K 行的表,该查询需要超过 15 分钟。我想知道是否有任何方法可以优化此性能。提前感谢您的回答/建议!

【问题讨论】:

您是否尝试过 4 个单独的查询并加入它们的结果?我想那会更快。年(期间)也在这里杀死你。将其设为单独的列,然后对其进行聚合。 这似乎不对。 . .除非其中一些关键列真的非常非常大。 为什么你使用year(),因为它已经是数值了? @Daniel Marcus 为什么你认为阅读整个表格 4 次会比只阅读一次更快? @user1330974,我认为 Yogesh 的观点是 period 已经是一个数字年份值,因此无需对其执行任何操作 【参考方案1】:

您对问题的描述向我建议grouping sets

SELECT YEAR([Period]) AS [Period], [Region], [Country], [Manufacturer], 
       SUM([Spend])
GROUP BY GROUPING SETS ( (YEAR([Period]),
                         (YEAR([Period]), [Region]),
                         (YEAR([Period]), [Region], [Country]), 
                         (YEAR([Period]), [Region], [Country], [Manufacturer])
                        );

我不知道这是否会更快,但它肯定看起来更符合您的问题。

【讨论】:

这行得通!查询在不到 10 秒内完成!! :) 非常感谢!【参考方案2】:

在这里使用 cross apply 来加快查询速度:

 SELECT 
     periodyear
    ,[Region]
    ,[Country]
    ,[Manufacturer]
    ,[Brand]
    ,SUM([Spend]) OVER (PARTITION BY  periodyear AS [SumOfSpendWorld]
    ,SUM([Spend]) OVER (PARTITION BY  periodyear, [Region]) AS [SumOfSpendRegion]
    ,SUM([Spend]) OVER (PARTITION BY  periodyear, [Region], [Country]) AS [SumOfSpendCountry]
    ,SUM([Spend]) OVER (PARTITION BY  periodyear, [Region], [Country], [Manufacturer]) AS [SumOfSpendManufacturer]
FROM myTable
  cross apply (select YEAR([Period]) periodyear) a

【讨论】:

@DanielMarcus。 . .您能否就您认为这可能对性能产生任何影响的原因提供任何见解? @GordonLinoff,我当然会听从你的意见 - 你不同意吗?我的想法是使用交叉应用,我们只需要解析一年(期间)一次,而不是在每个分区中多次解析。 @DanielMarcus。 . .并且即使在 480k 行上多次调用 year() 也不会导致 15 分钟的查询。相对于窗口函数的其他工作而言,这只是微不足道的。 @GordonLinoff 很高兴知道谢谢。那么为什么你认为 OP 查询需要 15 分钟呢? 。 .老实说,我不知道。鉴于问题的限制,它似乎太长了。【参考方案3】:

老派SUM() OVER()

SELECT 
      [Period]
    , [Region]
    , [Country]
    , [Manufacturer]
    , [Brand]
    , (SELECT SUM([Spend]) FROM myTable t WHERE e.[Period] = t.[Period] GROUP BY [Period]) AS [SumOfSpendWorld]
    , (SELECT SUM([Spend]) FROM myTable t WHERE e.[Period] = t.[Period] AND e.Region = t.Region GROUP BY [Period], [Region] ) AS [SumOfSpendRegion]
    , (SELECT SUM([Spend]) FROM myTable t WHERE e.[Period] = t.[Period] AND e.Region = t.Region AND e.Country = t.Country GROUP BY [Period], [Region], [Country] ) AS [SumOfSpendCountry]
    , (SELECT SUM([Spend]) FROM myTable t WHERE e.[Period] = t.[Period] AND e.Region = t.Region AND e.Country = t.Country AND e.Manufacturer = t.Manufacturer GROUP BY [Period], [Region], [Country], [Manufacturer] ) AS [SumOfSpendManufacturer]
FROM myTable e

虽然这不是一种优雅的方式,但它可以完成工作。我强烈建议您查看表格并对其进行分析,以了解哪些替代方法最适合您的情况。如果您觉得这是一条死胡同,那么我建议您使用临时表来加快速度。 例如,您可以根据句点选择行并使用批量复制将它们直接插入临时表,然后发挥您的作用。我见过迫使我使用临时表而不是简单的选择查询的表。其他人强迫我将桌子扩展成两张桌子。

所以,它并不总是干净整洁!

我希望这会给您提供另一种见解,对您的旅程有所帮助。

【讨论】:

谢谢。 :) 我现在开始意识到我无法实时运行此查询(即用户按需运行)。

以上是关于为几个分层组优化 SUM OVER PARTITION BY的主要内容,如果未能解决你的问题,请参考以下文章

sum over函数

MySQL - SUM() OVER() 函数用法详解

在SQL中按组计算移动平均数

SQL分组查询

Oracle分析函数Over()

Oracle分析函数Over()