Mei*_*i_R 6 mysql running-totals
我正在尝试根据 FIFO 获取数量结果,下面有 2 个表:
表购买:
| PO | Date | Quantity | Item |
|-------|--------------|----------|------|
| PO001 | 01-Jan-2016 | 3 | AO21 |
| PO002 | 10-Jan-2016 | 7 | AO21 |
| PO003 | 01-Feb-2016 | 3 | AO21 |
Run Code Online (Sandbox Code Playgroud)
表库存:
| SO | Date | Quantity | Item |
|-------|-------------|----------|------|
| SO001 | 02-Jan-2016 | 2 | AO21 |
| SO002 | 11-Feb-2016 | 8 | AO21 |
| SO003 | 12-Feb-2016 | 6 | AO23 |
Run Code Online (Sandbox Code Playgroud)
我希望输出是这样的:
| SO | PO | Quantity |
|-------|-------|----------|
| SO001 | PO001 | 2 |
| SO002 | PO001 | 1 |
| SO002 | PO003 | 7 |
Run Code Online (Sandbox Code Playgroud)
您对查看此输出的查询有任何想法吗?结果来自计算的 SO 和 PO 行。更多解释:
想要的结果中的 2、1、7 来自哪里?
从stock
和purchase
。item 的第一个(按日期)库存值为A021
2,第一次购买 ( PO001
) 需要 3,因此库存售出 2,我们在结果中得到这一行:
| SO001 | PO001 | 2 |
Run Code Online (Sandbox Code Playgroud)
我们仍然需要购买 1 个,然后下一个股票价值是 8。所以这次购买完成了,我们得到了 1 个(还有 7 个存货):
| SO002 | PO001 | 1 |
Run Code Online (Sandbox Code Playgroud)
下一次购买 ( PO002
) 需要 7 件,而我们还剩 7 件,因此购买已完成(该商品还剩 0 件库存)。我们得到:
| SO002 | PO003 | 7 |
Run Code Online (Sandbox Code Playgroud)
购买PO003
需要 3,但没有剩余库存,因此我们在该购买的结果中没有任何行。
这不是一个微不足道的问题,但对于窗口函数(和 CTE 以提高可读性)来说并不是很难。
MySQL 也没有实现,但让我们看看它是如何实现的:
WITH
running_purchase AS
( SELECT po, date, quantity, item,
SUM(quantity) OVER (PARTITION BY item
ORDER BY date, po
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
AS running_total
FROM purchase
),
running_stock AS
( SELECT so, date, quantity, item,
SUM(quantity) OVER (PARTITION BY item
ORDER BY date, so
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
AS running_total
FROM stock
)
SELECT
s.so, p.po, p.item,
LEAST(p.running_total, s.running_total)
- GREATEST(s.running_total - s.quantity, p.running_total - p.quantity)
AS quantity
FROM running_purchase AS p
JOIN running_stock AS s
ON p.item = s.item
AND s.running_total - s.quantity < p.running_total
AND p.running_total - p.quantity < s.running_total
ORDER BY
p.item, p.date, p.po ;
Run Code Online (Sandbox Code Playgroud)
在SQLFiddle(在 Postgres 中)测试。
请注意,MariaDB(可以替代 MySQL)已经宣布他们正在研究窗口函数和 CTE,可能是在他们的下一个版本 (10.2) 上。请参阅MariaDB 10.2 发行说明。
对于当前的 MySQL 版本,它必须更复杂,但逻辑是相同的:
SELECT
s.so, p.po, p.item,
LEAST(p.running_total, s.running_total)
- GREATEST(s.running_total - s.quantity, p.running_total - p.quantity)
AS quantity
FROM
( SELECT p1.po, p1.date, p1.quantity, p1.item,
SUM(p2.quantity) AS running_total
FROM purchase AS p1
JOIN purchase AS p2
ON p1.item = p2.item
AND ( p1.date > p2.date
OR p1.date = p2.date AND p1.po >= p2.po)
GROUP BY p1.item, p1.date, p1.po
) AS p
JOIN
( SELECT s1.so, s1.date, s1.quantity, s1.item,
SUM(s2.quantity) AS running_total
FROM stock AS s1
JOIN stock AS s2
ON s1.item = s2.item
AND ( s1.date > s2.date
OR s1.date = s2.date AND s1.so >= s2.so)
GROUP BY s1.item, s1.date, s1.so
) AS s
ON p.item = s.item
AND s.running_total - s.quantity < p.running_total
AND p.running_total - p.quantity < s.running_total
ORDER BY
p.item, p.date, p.po ;
Run Code Online (Sandbox Code Playgroud)
在SQLFiddle-2(在 MySQL 5.6 中)测试。