【问题标题】:SQL-sales sum over past N week including week with 0 sales过去 N 周的 SQL 销售额总和,包括销售额为 0 的一周
【发布时间】:2020-09-06 07:47:55
【问题描述】:

我想显示每件商品每周 4 周的销售额总和(当前周加上前 3 周)。

原始数据

+--------+--------+----------+
| Item   | Week   | sales    |
+--------+--------+----------+
|   a    |      1 |     10   |
|   a    |      2 |     10   |
|   a    |      4 |     10   |
|   a    |      7 |     10   |
|   a    |      8 |     10   |
|   a    |     10 |     10   |
|   b    |      1 |     10   |
|   b    |      2 |     10   |
|   b    |      4 |     10   |
|   b    |      7 |     10   |
|   b    |      8 |     10   |
|   b    |     10 |     10   |
+--------+--------+----------+

预期结果(以a项为例)

+------+------+------------------------------------------------------+
| Item | Week |                        sales                         |
+------+------+------------------------------------------------------+
| a    |    1 | 10                                                   |
| a    |    2 | 20                                                   |
| a    |    3 | 30                                                   |
| a    |    4 | 30                                                   |
| a    |    5 | 20(Note: sales of Week 5+Week 4+Week 3+Week 2)       |
| a    |    6 | 10                                                   |
| a    |    7 | 20[Note: 10(Week 7)+0(Week 6)+0(Week 5)+10 (Week 4)] |
| a    |    8 | 20                                                   |
| a    |    9 | 20                                                   |
| a    |   10 | 30(Note: sales of Week 10+Week 9+Week 8+Week 7)      |
+------+------+------------------------------------------------------+

我尝试使用

sum (sales) over (partition by item order by week row 3 preceding)

但是,它会跳过没有销售记录的周,并将所有非零值相加。例如:对于第 7 周,sum over 计算结果为 40,因为它将第 7、4、2、1 周的销售额相加。 有没有办法达到预期的效果?

【问题讨论】:

  • 显示您的代码。
  • 您使用的是哪种 DBMS 产品? “SQL”只是一种查询语言,而不是特定数据库产品的名称。请为您正在使用的数据库产品添加tag。 Why should I tag my DBMS
  • 感谢您的 cmets。我正在使用红移。

标签: sql amazon-redshift window-functions


【解决方案1】:

如果您只需要查看现有周的总和(因此不填充缺失的周),这可以在标准 SQL 中通过使用范围而不是行来实现:

select item, 
       week,
       sum (sales) over (partition by item order by week range between 3 preceding and current row)
from the_table
order by item, week;

如果您需要以行的形式填写缺失的周数,答案很大程度上取决于所使用的 DBMS。在 Postgres 中,您可以执行以下操作:

with items as (
  select distinct item
  from data
), all_weeks as (
  select i.item, g.week
  from items i
    cross join generate_series(1,10) as g(week)
)    
select t.item,
       t.week,
       sum (d.sales) over (partition by t.item order by t.week range between 3 preceding and current row)
from all_weeks t
  left join data d on d.item = t.item and d.week = t.week

对于其他 DBMS 系统,generate_series() 调用可以替换为固定的周数:

select i.item, g.week
from items i
  cross join ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) ) as g(week) 

Online example

【讨论】:

    【解决方案2】:

    由于问题没有提到 RDBMS,我在 SQL Server 中测试了下面的代码。以下解决方案适用于 SQL Server

    我正在做以下活动:

    1. 生成周、项目的所有组合
    2. 对于组合,生成前 4 周的总和(销售额)
    3. 删除原始列表中不存在的周
    DECLARE @table TABLE (Item CHAR(1), Week TINYINT, sales INT)
    
    INSERT INTO @table
    VALUES ('a', 1, 10), ('a', 2, 10), ('a', 4, 10), ('a', 7, 10), ('a', 8, 10), ('a', 10, 10), ('b', 1, 10), ('b', 2, 10), ('b', 4, 10), ('b', 7, 10), ('b', 8, 10), ('b', 10, 10);;
    
    WITH CTE_ItemWeek
    AS (
        SELECT Item, Week
        FROM (
            VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)
            ) AS t(week)
        CROSS JOIN (
            SELECT DISTINCT Item
            FROM @table
            ) A
        )
    SELECT *
    FROM (
        SELECT t1.Item, CASE 
                WHEN t2.Week IS NULL
                    THEN LAG(t2.week) OVER (
                            PARTITION BY t1.Item ORDER BY t2.week
                            )
                ELSE t2.week
                END AS week, SUM(t2.sales) OVER (
                PARTITION BY t1.Item ORDER BY t1.week ROWS BETWEEN 3 preceding
                        AND CURRENT ROW
                ) AS total_sales
        FROM CTE_ItemWeek AS t1
        LEFT OUTER JOIN @table AS t2 ON t1.Item = t2.Item
            AND t1.week = t2.week
        ) AS t
    WHERE week IS NOT NULL
    

    结果集

    +------+------+-------------+
    | Item | week | total_sales |
    +------+------+-------------+
    | a    |    1 |          10 |
    | a    |    2 |          20 |
    | a    |    4 |          30 |
    | a    |    7 |          20 |
    | a    |    8 |          20 |
    | a    |   10 |          30 |
    | b    |    1 |          10 |
    | b    |    2 |          20 |
    | b    |    4 |          30 |
    | b    |    7 |          20 |
    | b    |    8 |          20 |
    | b    |   10 |          30 |
    +------+------+-------------+
    

    【讨论】:

    • @a_horse_with_no_name,对不起。我应该检查一下。我错过并直接寻求解决方案。我的错。以后会注意的。
    • @a_horse_with_no_name,同意。用 RDBMS 信息更新了我的答案。
    【解决方案3】:

    每周,如果你的桌子是桌子

    SELECT sum(B.sales), A.week as curweek As totalsales
    FROM table A, table B
    WHERE curweek - B.week <4 
    AND curweek - B.week >=0
    GROUP BY curweek
    

    【讨论】:

    • 如果我只需要计算本周的数字,它就可以工作。但是,我需要计算每周的结果。感谢您的投入。
    【解决方案4】:
    SELECT week,Item ,sum(sales) WHERE (curweek - week < 4) group by week,Item 
    

    【讨论】:

    • 你能解释一下你的方法吗?
    • 欢迎来到 Stack Overflow!虽然这段代码可以解决问题,including an explanation 解决问题的方式和原因确实有助于提高帖子的质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的答案以添加解释并说明适用的限制和假设。 From Review
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    • 1970-01-01
    • 2015-09-16
    • 2017-05-12
    • 2017-11-04
    • 1970-01-01
    相关资源
    最近更新 更多