SQLazy: 从给定日期倒推原库存

问题描述

有一张库存计划表记录了某些日期的计划入库量 QTY 和入库后库存 CUSTQTY。现在给定一个目标日期(2024-02-26),需要从该日期向前倒推,每天减去当天的入库量,计算出每一天的原库存 UPDATED_CUSTQTY(即入库前库存),直到原库存归零为止。目标日期之后的记录不参与计算。

源数据

ITEM

LOC

NEEDDATE

QTY

CUSTQTY

ABC

XYZ

2024-02-29 00:00:00

0.6

3

ABC

XYZ

2024-02-28 00:00:00

0.6

3

ABC

XYZ

2024-02-27 00:00:00

0.6

3

ABC

XYZ

2024-02-26 00:00:00

0.6

3

ABC

XYZ

2024-02-23 00:00:00

0.6

3

ABC

XYZ

2024-02-22 00:00:00

0.6

3

ABC

XYZ

2024-02-21 00:00:00

0.6

3

ABC

XYZ

2024-02-20 00:00:00

0.6

3

ABC

XYZ

2024-02-19 00:00:00

0.6

3

ABC

XYZ

2024-02-16 00:00:00

0.6

3

ABC

XYZ

2024-02-15 00:00:00

0.6

3

ABC

XYZ

2024-02-14 00:00:00

0.6

3

ABC

XYZ

2024-02-13 00:00:00

4.8

3

期望结果

ITEM

LOC

NEEDDATE

QTY

CUSTQTY

UPDATED_CUSTQTY

ABC

XYZ

2024-02-29 00:00:00

0.6

3


ABC

XYZ

2024-02-28 00:00:00

0.6

3


ABC

XYZ

2024-02-27 00:00:00

0.6

3


ABC

XYZ

2024-02-26 00:00:00

0.6

3

2.4

ABC

XYZ

2024-02-23 00:00:00

0.6

3

1.8

ABC

XYZ

2024-02-22 00:00:00

0.6

3

1.2

ABC

XYZ

2024-02-21 00:00:00

0.6

3

0.6

ABC

XYZ

2024-02-20 00:00:00

0.6

3

0.0

ABC

XYZ

2024-02-19 00:00:00

0.6

3


ABC

XYZ

2024-02-16 00:00:00

0.6

3


ABC

XYZ

2024-02-15 00:00:00

0.6

3


ABC

XYZ

2024-02-14 00:00:00

0.6

3


ABC

XYZ

2024-02-13 00:00:00

4.8

3


以物料 ABC 在 LOC XYZ 的数据为例:

给定日期 2024-02-26,当日入库前原库存 = CUSTQTY - QTY = 3 - 0.6 = 2.4。往前倒推:每往前一天(如 2 月 23 日),原库存减掉当天的入库量 0.6(2.4 - 0.6 = 1.8),依此类推,倒推到 2024-02-20 时原库存归零(0.0)。

SQLazy 分步实现

核心思路:按日期降序排列,从给定日期开始,用 compute 的条件累加功能向前倒推库存:当天记为正基数(CUSTQTY - QTY),此前每天减去 QTY,之后的日期忽略。累加结果就是每天的原库存,归零之后不再输出。

Name

Anchor

Statement

warehouse


sort ITEM, LOC, NEEDDATE desc

t1

warehouse

compute if(NEEDDATE > datetime("2024-02-26 00:00:00") then 0, NEEDDATE = datetime("2024-02-26 00:00:00") then CUSTQTY - QTY; else -QTY) cum as cum_val

t2

t1

derive append if(round(cum_val, 2) >= 0 and NEEDDATE <= datetime("2024-02-26 00:00:00") then round(cum_val, 1) else null) as UPDATED_CUSTQTY; delete cum_val

【点击在线运行本例】

下面逐一解释这些步骤。

第 1 步:按物料、仓库分组并按日期降序排序

sort ITEM, LOC, NEEDDATE desc

将数据按 ITEM、LOC 排序,在每组内按 NEEDDATE 降序排列,确保同一物料从最新日期到最早日期顺序处理,以便后续从给定日期向前累加。

Picture1png

第 2 步:用条件表达式计算累计原库存(核心)

compute if(NEEDDATE > datetime("2024-02-26 00:00:00") then 0, NEEDDATE = datetime("2024-02-26 00:00:00") then CUSTQTY - QTY; else -QTY) cum as cum_val

这是最核心的一步。compute 的 if 表达式有三种分支:1. 给定日期之后的记录,累计值设为 0(不参与计算);2. 正好是给定日期的记录,累计值 = CUSTQTY - QTY(即当天的原库存);3. 给定日期之前的记录,每往前一天减掉当天的入库量(即 -QTY)。compute 的累加机制从上往下逐行累计,cum_val 就是倒推的当前原库存。

Picture2png

第 3 步:生成目标列

derive append if(round(cum_val, 2) >= 0 and NEEDDATE <= datetime("2024-02-26 00:00:00") then round(cum_val, 1) else null) as UPDATED_CUSTQTY; delete cum_val

用 derive append 添加 UPDATED_CUSTQTY 列:只对给定日期及之前的日期输出原库存(非负值),否则输出 null。round(cum_val, 2) >= 0 判断原库存是否归零或为正(负值不输出)。最后用 delete 删除辅助列 cum_val。

Picture3png

编译生成 SQL

确认上述步骤后,SQLazy 编译器自动生成原生 SQL(Oracle 语法):

WITH t2 AS (
    SELECT ITEM, LOC, NEEDDATE, QTY, CUSTQTY
        , SUM(CASE
            WHEN NEEDDATE > TO_TIMESTAMP("2024-02-26 00:00:00", "YYYY-MM-DD HH24:MI:SS") THEN 0
            WHEN NEEDDATE = TO_TIMESTAMP("2024-02-26 00:00:00", "YYYY-MM-DD HH24:MI:SS") THEN CUSTQTY - QTY
            ELSE -QTY
        END) OVER (ORDER BY ITEM ASC NULLS FIRST, LOC ASC NULLS FIRST, NEEDDATE DESC ROWS UNBOUNDED PRECEDING) AS cum_val
    FROM warehouse
)
SELECT ITEM, LOC, NEEDDATE, QTY, CUSTQTY
    , CASE
        WHEN round(cum_val, 2) >= 0
            AND NEEDDATE <= TO_TIMESTAMP("2024-02-26 00:00:00", "YYYY-MM-DD HH24:MI:SS")
        THEN round(cum_val, 2)
        ELSE NULL
    END AS UPDATED_CUSTQTY
FROM t2
ORDER BY ITEM ASC NULLS FIRST, LOC ASC NULLS FIRST, NEEDDATE DESC

SQLazy 让你用业务语言描述逻辑,而不是用 SQL 语法写嵌套查询。这个 "从给定日期倒推原库存" 的例子,核心就是 compute 的三分支条件累加,直接将 "当天记基值、此前减量、之后忽略" 的业务规则写成 if 表达式,由 compute 自动完成逐行累计。如果手写 SQL,你需要用 SUM() OVER 配合 CASE WHEN 构造窗口函数,并正确指定 ROWS UNBOUNDED PRECEDING 的窗口范围,稍有疏忽就会出错。SQLazy 的分步计算让每步都可独立验证中间结果,降低了复杂逻辑的出错概率。

官方链接

SQLazy 在线体验:sqlazy.com(免费,无需注册)
SQLazy 项目仓库:github.com/SPLWare/SQLazy