这是我通常会在 python 中解决的问题,但想看看是否可以在 SQL 中有效地完成。
我有一个示例预测销售数据集,如下所示:
项目 | 位置_id | 预测 | 周_开始 | 包装尺寸 | 行号 |
---|---|---|---|---|---|
绿色铅笔 | 1234 | 0.82 | 12/3/23 | 6 | 1 |
绿色铅笔 | 1234 | 0.82 | 12/10/23 | 6 | 2 |
绿色铅笔 | 1234 | 0.82 | 12/17/23 | 6 | 3 |
绿色铅笔 | 1234 | 1.23 | 12/24/23 | 6 | 4 |
绿色铅笔 | 1234 | 1.23 | 12/31/23 | 6 | 5 |
绿色铅笔 | 1234 | 1.23 | 1/7/24 | 6 | 6 |
蓝色标记 | 999 | 4.31 | 12/3/23 | 6 | 1 |
蓝色标记 | 999 | 6.47 | 12/10/23 | 6 | 2 |
蓝色标记 | 999 | 6.47 | 12/17/23 | 6 | 3 |
蓝色标记 | 999 | 6.47 | 12/24/23 | 6 | 4 |
蓝色标记 | 999 | 6.47 | 12/31/23 | 6 | 5 |
蓝色标记 | 999 | 6.47 | 1/7/24 | 6 | 6 |
该数据是未来6周不同商品进入不同商店的预测销售量。行号已添加到每个项目/位置/周。
pack_size
列是需要库存时发送到商店的单位数量。
目标是创建两列
units_to_store
和 week_end_inventory
来确定何时应根据预测销量向商店发送一包商品。理想的最终状态数据集如下(仅适用于 green_pencil,但希望每个项目/商店组合都有它):
项目 | 位置_id | 预测 | 周_开始 | 包装尺寸 | 行号 | 商店单位 | 周末库存 |
---|---|---|---|---|---|---|---|
绿色铅笔 | 1234 | 1.43 | 12/3/23 | 6 | 1 | 6 | 4.57 |
绿色铅笔 | 1234 | 2.67 | 12/10/23 | 6 | 2 | 0 | 1.9 |
绿色铅笔 | 1234 | 0.82 | 12/17/23 | 6 | 3 | 0 | 1.08 |
绿色铅笔 | 1234 | 1.23 | 12/24/23 | 6 | 4 | 6 | -0.15 |
绿色铅笔 | 1234 | 1.23 | 12/31/23 | 6 | 5 | 0 | 4.77 |
绿色铅笔 | 1234 | 1.23 | 1/7/24 | 6 | 6 | 0 | 3.54 |
第一周始终以
pack_size
项目开始,在本例中为 6 个单位。然后,目标是获取预测销售额并获得运行总和,直到该数字低于 0,此时应将新包装发送到商店,并且运行总和以 pack_size
重新开始。第一周的 week_end_inventory
将是 pack_size
- forecast
,然后将在接下来的行/周中减去 forecast
,直到它低于 0,并且该过程将重新开始。
做到这一点的最佳方法是什么?我尝试过使用
lag
函数,但在需要动态填充值时遇到了麻烦。
以下假设是 MySQL(但请注意,“row_number”列会导致问题,因此有必要对该列使用反引号。这只是部分解决方案,但目前已经没有时间了。
CREATE TABLE mytable (
item VARCHAR(200)
, location_id INTEGER
, forecast DECIMAL(10, 2)
, week_begin DATE
, pack_size INTEGER
, `row_number` INTEGER
);
INSERT INTO mytable (item, location_id, forecast, week_begin, pack_size, `row_number`)
VALUES
('green_pencil', 1234, 0.82, STR_TO_DATE('12/3/23', '%m/%d/%y'), 6, 1),
('green_pencil', 1234, 0.82, STR_TO_DATE('12/10/23', '%m/%d/%y'), 6, 2),
('green_pencil', 1234, 0.82, STR_TO_DATE('12/17/23', '%m/%d/%y'), 6, 3),
('green_pencil', 1234, 1.23, STR_TO_DATE('12/24/23', '%m/%d/%y'), 6, 4),
('green_pencil', 1234, 1.23, STR_TO_DATE('12/31/23', '%m/%d/%y'), 6, 5),
('green_pencil', 1234, 1.23, STR_TO_DATE('1/7/24', '%m/%d/%y'), 6, 6),
('blue_marker', 999, 4.31, STR_TO_DATE('12/3/23', '%m/%d/%y'), 6, 1),
('blue_marker', 999, 6.47, STR_TO_DATE('12/10/23', '%m/%d/%y'), 6, 2),
('blue_marker', 999, 6.47, STR_TO_DATE('12/17/23', '%m/%d/%y'), 6, 3),
('blue_marker', 999, 6.47, STR_TO_DATE('12/24/23', '%m/%d/%y'), 6, 4),
('blue_marker', 999, 6.47, STR_TO_DATE('12/31/23', '%m/%d/%y'), 6, 5),
('blue_marker', 999, 6.47, STR_TO_DATE('1/7/24', '%m/%d/%y'), 6, 6);
Records: 12 Duplicates: 0 Warnings: 0
SELECT
item
, location_id
, forecast
, week_begin
, pack_size
, `row_number`
, CASE
WHEN LAG(pack_size, 1, pack_size) OVER (PARTITION BY item, location_id ORDER BY week_begin, `row_number`) - forecast >= 0
THEN LAG(pack_size, 1, pack_size) OVER (PARTITION BY item, location_id ORDER BY week_begin, `row_number`) - forecast
ELSE pack_size - forecast
END AS units_to_store
, CASE
WHEN LAG(pack_size, 1, pack_size) OVER (PARTITION BY item, location_id ORDER BY week_begin, `row_number`) - forecast < 0
THEN 0
ELSE LAG(pack_size, 1, pack_size) OVER (PARTITION BY item, location_id ORDER BY week_begin, `row_number`) - forecast
END AS week_end_inventory
FROM mytable;
项目 | 位置_id | 预测 | 周_开始 | 包装尺寸 | 行号 | 商店单位 | 周末库存 |
---|---|---|---|---|---|---|---|
蓝色标记 | 999 | 4.31 | 2023-12-03 | 6 | 1 | 1.69 | 1.69 |
蓝色标记 | 999 | 6.47 | 2023-12-10 | 6 | 2 | -0.47 | 0.00 |
蓝色标记 | 999 | 6.47 | 2023-12-17 | 6 | 3 | -0.47 | 0.00 |
蓝色标记 | 999 | 6.47 | 2023-12-24 | 6 | 4 | -0.47 | 0.00 |
蓝色标记 | 999 | 6.47 | 2023-12-31 | 6 | 5 | -0.47 | 0.00 |
蓝色标记 | 999 | 6.47 | 2024-01-07 | 6 | 6 | -0.47 | 0.00 |
绿色铅笔 | 1234 | 0.82 | 2023-12-03 | 6 | 1 | 5.18 | 5.18 |
绿色铅笔 | 1234 | 0.82 | 2023-12-10 | 6 | 2 | 5.18 | 5.18 |
绿色铅笔 | 1234 | 0.82 | 2023-12-17 | 6 | 3 | 5.18 | 5.18 |
绿色铅笔 | 1234 | 1.23 | 2023-12-24 | 6 | 4 | 4.77 | 4.77 |
绿色铅笔 | 1234 | 1.23 | 2023-12-31 | 6 | 5 | 4.77 | 4.77 |
绿色铅笔 | 1234 | 1.23 | 2024-01-07 | 6 | 6 | 4.77 | 4.77 |