过去N周(包括一周内有0个销售)的SQL销售总额

时间:2020-05-20 03:45:09

标签: sql amazon-redshift window-functions

我想显示每个项目每周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周,总和将第7、4、2、1周的销售额相加得出的结果为40。 有没有办法达到预期的结果?

4 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

SELECT week,Item ,sum(sales) WHERE (curweek - week < 4) group by week,Item 

答案 2 :(得分:0)

由于该问题未提及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 |
+------+------+-------------+

答案 3 :(得分:0)

如果您只需要查看现有几周的总和(因此不必填写缺失的几周),则可以在标准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