在 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 表达式来得到想要的结果:
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
.
语句更难理解
这是我的 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 表达式来得到想要的结果:
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
.