在 Oracle ROLLUP 中组合 MAX 和 SUM

Combining MAX and SUM in an Oracle 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
)
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 行代码可显着简化其维护工作。

可以改用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);

您可以使用 window 函数结合 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;

您可以使用 Union,因此总数 select 将仅为 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);

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

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 语句比 multi-level SELECT.

语句更难理解