SQL 查询使用 Oracle SQL 窗口函数以部分数量填充多个需求

标签 sql oracle window-functions

我想解决一个在我的 SQL 历史中多次出现的抽象问题。如果我们想象我们是一家销售公司并谈论我们想要销售的零件(产品),那么这个抽象的问题可以更好地实现,因此在不同的日期需要不同的数量。 另一方面,我们有我们的“填充可能性”,例如来自不同日期和数量的库存、生产、采购的数量。

我想只使用一个查询(没有过程,没有临时表)来满足按所需日期和可用日期排序的需求。

作为技术基础,您可以假设有两个表:

  • NEED_TABLE,其中列出了多项需求、所需日期和数量。
  • FILL_TABLE,其中列出了多个“填充”、可用日期和数量。

在我的例子中有两个需求:

  • 需要 A:我们需要 partno 123,数量为 4,日期为 01/02/2019
  • 需要 B:我们需要 partno 123,数量为 2,日期为 06/02/2019

我们有两个不同数量的“填充物”:

  • 填写 X:我们在采购订单中有 partno 123,数量为 2,可用时间为 01/01/2019
  • 填写 Y:我们在采购订单中有 partno 123,数量为 4,可用时间为 06/01/2019

结果应该是:

  • 我在 2019 年 1 月 2 日需要数量为 4 的 partno 123(“需要 A”),它由数量为 2 的采购订单(“Fill X”)和另一个数量为 2 的采购订单(“Fill Y"- 部分)。
  • 我在 2019 年 6 月 2 日需要数量为 2 的 partno 123(“需要 B”),它由数量为 2 的采购订单填写(“填写 Y”- 部分)。

SQL查询:

with
    NEED_TABLE
    as
        (select 'A' NEED_ID, 123 partno, to_date('01/02/2019', 'MM/DD/YYYY') DATE_NEEDED, 4 NEED_QTY from dual
         union all
         select 'B' NEED_ID, 123 partno, to_date('06/02/2019', 'MM/DD/YYYY') DATE_NEEDED, 2 NEED_QTY from dual),
    FILL_TABLE
    as
        (select 'X' FILL_ID, 123 partno, to_date('01/01/2019', 'MM/DD/YYYY') DATE_AVAILABLE, 2 FILL_QTY from dual
         union all
         select 'Y' FILL_ID, 123 partno, to_date('06/01/2019', 'MM/DD/YYYY') DATE_AVAILABLE, 4 FILL_QTY from dual)
select   NEED_TABLE.NEED_ID
       , NEED_TABLE.PARTNO
       , NEED_TABLE.DATE_NEEDED
       , NEED_TABLE.NEED_QTY
       , FILL_TABLE.FILL_ID
       , FILL_TABLE.DATE_AVAILABLE
       , FILL_TABLE.FILL_QTY
       /* all following is wrong/incomplete */
       , lag(need_QTY - fill_QTY, 1, need_QTY)
             over(
                 partition by NEED_ID
                 order by DATE_NEEDED, DATE_AVAILABLE) REAL_NEED_QTY
       , greatest(
               lag(need_QTY - fill_QTY, 1, need_QTY)
                   over(
                       partition by NEED_ID
                       order by DATE_NEEDED, DATE_AVAILABLE)
             - FILL_QTY
           , 0) LEFT_NEED_QTY
       , abs(
             least(
                   lag(need_QTY - fill_QTY, 1, need_QTY)
                       over(
                           partition by NEED_ID
                           order by DATE_NEEDED, DATE_AVAILABLE)
                 - FILL_QTY
               , 0)) LEFT_FILL_QTY
    from NEED_TABLE, FILL_TABLE
order by DATE_NEEDED, DATE_AVAILABLE;

如果您检查该查询的结果,第一个 NEED_ID“A”似乎一切正常。但是当它继续使用 NEED_ID“B”时,它不记得在填充需求“A”时 FILL_ID X 和 Y 已经减少。

我期望这样的结果:

NEED_ID A is filled by FILL_ID X qty 2

NEED_ID A is filled by FILL_ID Y qty 2

(NEED_ID A is filled by FILL_ID X qty 0)

NEED_ID B is filled by FILL_ID Y qty 2

需要_表:

| NEED_ID | PARTNO | DATE_NEEDED | NEED_QTY |
|---------|--------|-------------|----------|
| A       | 123    | 01/02/2019  | 4        |
| B       | 123    | 06/02/2019  | 2        |

FILL_TABLE:

| FILL_ID | PARTNO | DATE_AVAILABLE | FILL_QTY |
|---------|--------|----------------|----------|
| X       | 123    | 01/01/2019     | 2        |
| Y       | 123    | 06/01/2019     | 4        |

预期查询结果:

| NEED_ID | PARTNO | DATE_NEEDED | NEED_QTY | FILL_ID | DATE_AVAILABLE | FILL_QTY | ***REAL_FILL*** | "WHY?"                                                              |
|---------|--------|-------------|----------|---------|----------------|----------|-----------------|---------------------------------------------------------------------|
| A       | 123    | 01/02/2019  | 4        | X       | 01/01/2019     | 2        | 2               | A needs 4, gets partially filled by X by 2                          |
| A       | 123    | 01/02/2019  | 4        | Y       | 06/01/2019     | 4        | 2               | A still needs 2, gets completely filled by Y by 2                   |
| B       | 123    | 06/02/2019  | 2        | X       | 01/01/2019     | 2        | 0               | B needs 2, can't get filled by X, because A already used that qty   |
| B       | 123    | 06/02/2019  | 2        | Y       | 06/01/2019     | 4        | 2               | B still needs 2, gets completely filled by remaining qty of Y, by 2 |

非常感谢任何帮助 - 谢谢!

最佳答案

这是我的尝试:

with 
  need(rn, nid, nq) as (
    select 1, 'A', 4 from dual union all 
    select 2, 'B', 2 from dual ),
  fill(rf, fid, fq) as (
    select 1, 'X', 2 from dual union all 
    select 2, 'Y', 4 from dual ),
  u as (
    select rn, nid, -nq nq, null rf, null fid, null fq from need
    union all select null, null, null, rf, fid, fq from fill),
  c (crn, cnid, cnq, crf, cfid, cfq, rest, amt) as (
    select rn, nid, nq, 0, fid, fq, nq, 0 from u where rn = 1
    union all 
    select nvl(rn, crn), nvl(nid, cnid), nvl(nq, cnq), 
           nvl(rf, crf), nvl(fid, cfid), nvl(fq, cfq), rest + nvl(nq, fq), 
           least(abs(rest), abs(nvl(nq, fq)))
      from c 
      join u on rest >= 0 and rn = crn + 1 
             or rest <  0 and rf = crf + 1 )
select cnid, cfid, amt from c where amt <> 0

我简化了数据,但是 partno 可以很容易地添加到连接和 partition by 中,适当的 row_numbers 和日期仅对排序行很重要。如果它们有更多含义,现在可以添加它们,但让我们从更清晰的内容开始。

它是如何工作的。 needfill 是我们的数据源。 u 是这些表的联合,需要填充 数据在单独的列中。需要此联合才能使下一个查询正常工作。

c 是从第一个fill 开始的递归 CTE。它是我们的 anchor 。在下一步中,我添加(加入)fillneed 行,具体取决于我们在之前的 rest 中得到的内容。如果 rest 小于零,则意味着我们必须寻找下一个 fill 行。 如果它更大,则意味着我们从 fills 中获得了盈余,我们可以寻找下一个需要。在每一步中,交易的金额都被计算在内,等于之前剩余和当前加入的填充/需求的较低值。

最后,我获取金额和交易双方。在一些示例上进行了测试。

demo

关于SQL 查询使用 Oracle SQL 窗口函数以部分数量填充多个需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56061093/

相关文章:

sql - 如何使用条件组明智地将列值替换为其先前的值

sql - 检查变量是否在SQL Server中包含任何非数字数字

sql - 如何返回 COUNT(*) 并将其存储在变量中以便在 VBscript 中进一步使用

sql - COALESCE 接受多少个参数?

sql - Oracle APEX 4.2 经典报告中的自定义搜索

sql-server - SQL Server Over Partition By 和 Group By 性能比较

sql - 如何在 PostgreSQL 中将状态日志数据聚合到具有相同状态的时间间隔?

php - 创建内部联接以创建正确的输出

mysql - 如何找到层次树中的所有子节点

xml - 将超过 4000 个字符的 XML 插入到 Oracle XMLTYPE 列中