我在Postgres中有一个表p1
,其中包含以下内容:
| id | product_id | transaction_date | quantity |
|----|------------|------------------|----------|
| 1 | 1 | 2015-01-01 | 1 |
| 2 | 1 | 2015-01-02 | 2 |
| 3 | 1 | 2015-01-03 | 3 |
和p2
表包含以下产品:
| id | product | stock |
|----|--------------|-------|
| 1 | Product A | 15 |
对于stock
中的每个新记录,p2
'中的p1
已被缩减。
如何重建以前的状态以获得此结果?
| product | first_stock | quantity | last_stock |
|-----------|-------------|----------|------------|
| Product A | 21 | 1 | 20 |
| Product A | 20 | 2 | 18 |
| Product A | 18 | 3 | 15 |
我尝试使用lead()
获取当前行之后的数量。
SELECT p2.product, p1.quantity, lead(p1.quantity) OVER(ORDER BY p1.id DESC)
FROM p1 INNER JOIN p2 ON p1.product_id = p2.id;
但是如何计算当前股票的前导行?
答案 0 :(得分:3)
您不需要lead()
,您需要在所有行之间运行总和,以便从交易数据中重建以前的状态:
SELECT p2.product
, p2.stock + px.sum_quantity AS first_stock
, px.quantity
, p2.stock + px.sum_quantity - quantity AS last_stock
FROM p2
JOIN (
SELECT product_id, quantity, transaction_date
, sum(quantity) OVER (PARTITION BY product_id
ORDER BY transaction_date DESC) AS sum_quantity
FROM p1
) px ON px.product_id = p2.id
ORDER BY px.transaction_date;
假设事件过程实际由transaction_date
表示。
使用聚合函数sum()
作为窗口聚合函数来获取运行总和。使用子查询,因为我们多次使用数量的运行总和(sum_quantity
)
对于last_stock
减去当前行的quantity
(在冗余添加之后)。
理论上,使用窗口框架的自定义框架定义仅对前面行的数量求和会更便宜,因此我们不要添加和减去<的数量< em>当前行冗余。但这实际上更复杂,而且速度更快:
SELECT p2.id, p2.product, px.transaction_date -- plus id and date for context
, p2.stock + COALESCE(px.pre_sum_q + px.quantity, 0) AS first_stock
, px.quantity
, p2.stock + COALESCE(px.pre_sum_q, 0) AS last_stock
FROM p2
LEFT JOIN (
SELECT id, product_id, transaction_date
, quantity
, sum(quantity) OVER (PARTITION BY product_id
ORDER BY transaction_date DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS pre_sum_q
FROM p1
) px ON px.product_id = p2.id
ORDER BY px.transaction_date, px.id;
此相关答案中的帧定义说明:
在此过程中,还展示了如何使用LEFT JOIN
和COALESCE
来防止错误的行和NUll值用于不具有任何相关行的产品p1
,如果同一天的同一产品有多个交易,则为稳定的排序顺序。
仍然假设要定义的所有列都是NOT NULL,或者你需要为具有NULL值的极端情况做更多的事情。