oracle 在SQL中,有没有一种优雅的方法可以在聚合后使用不同的选择标准进行联合

qzlgjiam  于 7个月前  发布在  Oracle
关注(0)|答案(3)|浏览(67)

例如,我有以下销售表:
| 周|销售|
| --|--|
| 1 | 10 |
| 1 | 15 |
| 2 | 5 |
| 2 | 10 |
| 3 | 10 |
| 3 | 20 |
| 4 | 5 |
| 4 | 10 |
| 5 | 15 |
| 5 | 10 |
我想要一个这样的SQL查询:

SELECT '1-3' AS period, SUM(sales)
FROM sale
WHERE 1<=week AND week<=3
GROUP BY period

UNION ALL

SELECT '2-4' AS period, SUM(sales)
FROM sale
WHERE 2<=week AND week<=4
GROUP BY period

UNION ALL

SELECT '3-5' AS period, SUM(sales)
FROM sale
WHERE 3<=week AND week<=5
GROUP BY period

字符串
当滚动窗口的数量很大时,上面的SQL查询会很麻烦,有解决方法吗?
我试着考虑使用卡特里亚产品。但是,它会产生一些不必要的行。

hs1ihplo

hs1ihplo1#

一个简单而有效的解决方案是使用带有滚动窗口框架子句的窗口函数。但是,如果数据中缺少周数(例如第1,3,4周),则框架将被错误计算或某些预期数据将丢失。
一个完整的解决方案是使用一些行生成器技术(例如递归cte)添加缺失的周数,然后使用窗口函数计算滚动窗口的总和:

WITH rcte(week, maxval) AS (
    SELECT MIN(week), MAX(week) - 2
    FROM sale
    UNION ALL
    SELECT week + 1, maxval
    FROM rcte
    WHERE week + 1 <= maxval
), cte AS (
    SELECT week, sales
    FROM sale
    UNION ALL
    SELECT week, 0
    FROM rcte
)
SELECT
    CONCAT(week, '-', week + 2) AS period,
    SUM(SUM(sales)) OVER (ORDER BY week ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS total
FROM cte
GROUP BY week
ORDER BY week

字符串
这种方法应该适用于所有支持递归CTE和窗口函数的RDBMS。
DB<>Fiddle

axr492tv

axr492tv2#

一个选项是使用子查询定义时段(使用LEVEL Connect By)并获取时段的总和:

WITH    --  Sample Data
    sales (WEEK, SALE) AS
        (   Select  1,  10  From Dual   Union All
            Select  1,  15  From Dual   Union All
            Select  2,  5   From Dual   Union All
            Select  2,  10  From Dual   Union All
            Select  3,  10  From Dual   Union All
            Select  3,  20  From Dual   Union All
            Select  4,  5   From Dual   Union All
            Select  4,  10  From Dual   Union All
            Select  5,  15  From Dual   Union All
            Select  5,  10  From Dual 
        )

个字符

wnavrhmk

wnavrhmk3#

如果你有分析窗口功能,它很容易,
假设salestable包含数据

SELECT
    [week]
   ,LAG([week], 2) OVER (ORDER BY [week]) pweek
   ,SUM(SUM(sales)) OVER (ORDER BY [week] ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) sales

FROM salestable
GROUP BY [week]

字符串
这将给予连续三个月的总和,其中pweek包含startweek,sales表示值
如果之前的周数少于2周,pweek将为null。

+------+-------+-------+
| week | pweek | sales |
+------+-------+-------+
|    1 | null  |    25 |
|    2 | null  |    40 |
|    3 | 1     |    70 |
|    4 | 2     |    60 |
|    5 | 3     |    70 |
+------+-------+-------+


所以我把它 Package 在一个新的查询中,过滤掉pweek中的null,并格式化范围:

SELECT
    CAST(pweek AS VARCHAR(10)) + '-' + CAST([week] AS VARCHAR(10)) period
   ,sales
FROM (SELECT
        [week]
       ,LAG([week], 2) OVER (ORDER BY [week]) pweek
       ,SUM(SUM(sales)) OVER (ORDER BY [week] ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) sales

    FROM salestable
    GROUP BY [week]) a
WHERE a.pweek IS NOT NULL
AND [week] - a.pweek = 2

+-------+-------+
| period| sales |
+-------+-------+
| 1-3   |    70 |
| 2-4   |    60 |
| 3-5   |    70 |
+-------+-------+


最后一个条件“week-pweek=2”是处理缺失的周。
如果你没有第4周的数据,那么这个查询会给我们给予2-5的范围,现在它会忽略它。
为了解决这个问题,我会填写周数,确保如果我在一周内没有数据,销售额将为零。但这超出了这个范围。
SQL Fiddle在这里

相关问题