【问题标题】:Combining MAX and SUM in an Oracle ROLLUP在 Oracle ROLLUP 中结合 MAX 和 SUM
【发布时间】:2020-09-08 18:14:19
【问题描述】:

这是我的 SQL:

WITH source1 AS (
   SELECT 'Fruit' foodtype, 'Apple'  food, 20 weight FROM dual UNION
   SELECT 'Fruit' foodtype, 'Apple'  food, 30 weight FROM dual UNION
   SELECT 'Fruit' foodtype, 'Grape'  food, 1  weight FROM dual UNION
   SELECT 'Veg'   foodtype, 'Carrot' food, 40 weight FROM dual UNION
   SELECT 'Veg'   foodtype, 'Leek'   food, 20 weight FROM dual
)
SELECT grouping(food) lv, foodtype, food, max(weight) weight
FROM   source1
GROUP BY foodtype, ROLLUP (food);

输出如下:

LV FOODTYPE FOOD   WEIGHT
-- -------- ------ ------
0  Veg      Leek       20
0  Veg      Carrot     40
1  Veg                 40
0  Fruit    Apple      30
0  Fruit    Grape       1
1  Fruit               30

我期待它看起来像这样:

LV FOODTYPE FOOD   WEIGHT
-- -------- ------ ------
0  Veg      Leek       20
0  Veg      Carrot     40
1  Veg                 60
0  Fruit    Apple      30
0  Fruit    Grape       1
1  Fruit               31

换句话说,我希望 rollup 总结每种食物的最大重量,而不是取食物类型类别中所有最大值中的最大值。

我确实有各种各样的解决方案,但这意味着必须添加一层额外的 SQL 语句嵌套:

WITH source1 AS (
   SELECT 'Fruit' foodtype, 'Apple'  food, 20 weight FROM dual UNION
   SELECT 'Fruit' foodtype, 'Apple'  food, 30 weight FROM dual UNION
   SELECT 'Fruit' foodtype, 'Grape'  food, 1  weight FROM dual UNION
   SELECT 'Veg'   foodtype, 'Carrot' food, 40 weight FROM dual UNION
   SELECT 'Veg'   foodtype, 'Leek'   food, 20 weight FROM dual
), source_grp AS (
   SELECT s.foodtype, s.food, max(s.weight) max_weight
   FROM   source1 s
   GROUP BY foodtype, food
)
SELECT grouping(g.food) lv, g.foodtype, g.food, sum(g.max_weight) weight
FROM   source_grp g
GROUP BY g.foodtype, ROLLUP (g.food);

有没有办法在没有额外嵌套的情况下做到这一点?

当然,从我的实际情况来看,这个例子已经大大简化了,这就是为什么我试图找到一种方法来减少代码行数。从长远来看,将一条 SQL 语句减少 60 行代码可显着简化其维护。

【问题讨论】:

    标签: sql oracle oracle12c


    【解决方案1】:

    几天后重温。可以这样做:

    WITH source1 AS (
       SELECT 'Fruit' foodtype, 'Apple'  food, 20 weight FROM dual UNION
       SELECT 'Fruit' foodtype, 'Apple'  food, 30 weight FROM dual UNION
       SELECT 'Fruit' foodtype, 'Grape'  food, 1  weight FROM dual UNION
       SELECT 'Veg'   foodtype, 'Carrot' food, 40 weight FROM dual UNION
       SELECT 'Veg'   foodtype, 'Leek'   food, 20 weight FROM dual
    )
    SELECT grouping(s.food) lv, s.foodtype, s.food,
           CASE WHEN grouping(s.food)=1 THEN
              sum(CASE WHEN grouping(s.food)=1 THEN 0 ELSE max(s.weight) END) OVER (PARTITION BY s.foodtype ORDER BY s.food)
           ELSE
              max(s.weight)
           END weight
    FROM   source1 s
    GROUP BY s.foodtype, ROLLUP (s.food)
    

    说实话,我也不是 100% 肯定我喜欢这个答案。根据上下文和从维护的角度来看,CASE-WHEN 语句比多级 SELECT 更难理解。

    【讨论】:

      【解决方案2】:

      你可以使用窗口函数结合CASE表达式来得到想要的结果:

      Demo

      WITH cte1 AS
      (SELECT a.*, 
      SUM(weight) OVER(PARTITION BY foodtype ORDER BY lv, food rows between unbounded preceding and 1 preceding) cum_sum
      FROM table1 a)
      SELECT lv, foodtype, food, 
      CASE WHEN lv = 1 
           THEN cum_sum
           ELSE weight END AS weight
      FROM cte1;
      

      您可以使用联合,因此总选择将只有 2。

      WITH source1 AS (
         SELECT 'Fruit' foodtype, 'Apple'  food, 20 weight FROM dual UNION
         SELECT 'Fruit' foodtype, 'Apple'  food, 30 weight FROM dual UNION
         SELECT 'Fruit' foodtype, 'Grape'  food, 1  weight FROM dual UNION
         SELECT 'Veg'   foodtype, 'Carrot' food, 40 weight FROM dual UNION
         SELECT 'Veg'   foodtype, 'Leek'   food, 20 weight FROM dual
      )
      SELECT foodtype, food, max(weight) weight
      FROM   source1
      GROUP BY foodtype, food
      UNION
      (SELECT DISTINCT FOODTYPE, NULL, SUM(WEIGHT) TOTAL_WEIGHT  FROM SOURCE1 GROUP BY FOODTYPE);
      

      【讨论】:

      • 分解后,这实际上比我在问题中建议的答案更复杂。您已将列 lv 添加到源表中,而实际上它根本不是源数据的一部分。要首先获得lv 值,您首先需要对源数据进行分组。所以事实上,你最终得到了 4 个 SELECT 语句,而不是我的 3 个(或者 3 个而不是 2 个,这取决于你如何计算)。虽然巧妙地使用了ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
      • @cartbeforehorse,我已经更新了我的答案并提供了一个替代解决方案,总共选择 2。
      • 但这给出了错误的输出。当我期待31 时,水果总数给了我51 的答案。您仍然需要对源进行分组。
      【解决方案3】:

      可以改用row_number()

      SELECT GROUPING(food) as lv, foodtype, food, SUM(weight) weight
      FROM (SELECT s1.*, 
                   ROW_NUMBER() OVER (PARTITION BY foodtype, food ORDER BY weight DESC) as seqnum
            FROM source1 s1
           ) s1
      WHERE sequm = 1;
      GROUP BY foodtype, ROLLUP (food);
      

      【讨论】:

      • 恐怕这只是给出了错误的输出。我只从每个foodtype 中得到第一个food
      • @cartbeforehorse 。 . .您只需要修复partition by 子句。我更新了答案。
      • 即使您稍微摆弄一下,您使用的 SELECT 语句数量也不会比我在问题中的数量少。您只是以不同的方式嵌套它们。
      猜你喜欢
      • 2018-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-04
      • 2022-01-13
      • 1970-01-01
      • 2017-04-22
      相关资源
      最近更新 更多