如何根据老化支架数量将单个单元格的库存数量分配到不同老化支架?

How to distribute stock quantity from a single cell to different aging brackets based on the the aging brackets quantities?

我们有一个 table,其中包含项目代码和当前库存数量以及收到时(1-90、91-120 等)在时效括号中的数量,如下所示:

我需要分配 STOCK_AS_ON_DATE 个年龄段的数量,直到没有余额为止。

示例:

项目 A 的 STOCK_AS_ON_DATE 是 40377,在 A1TO90 列中我们可以看到收到了 4000 数量,在 A91TO120,收到了 4000 数量,在 A121TO270,收到了 13000 个数量,我们想在其他时效字段中以这种方式分配 STOCK_AS_ON_DATE 个数量,直到所有时效字段的总和等于 STOCK_AS_ON_DATE。在这个项目的情况下,当我们到达列 A631TO720 时,1377 数量余额从 40377 中遗漏。如果任何老化括号值为 0,我们必须保持原样并继续前进。

下面是创建示例 table 和数据的 Oracle 查询:

CREATE TABLE AA_STK_AGING
(
  ITEM              NVARCHAR2(10),
  STOCK_AS_ON_DATE  NUMBER,
  A1TO90            NUMBER,
  A91TO120          NUMBER,
  A121TO270         NUMBER,
  A271TO360         NUMBER,
  A361TO450         NUMBER,
  A451TO540         NUMBER,
  A541TO630         NUMBER,
  A631TO720         NUMBER,
  A721PLUS          NUMBER
)
LOGGING 
NOCOMPRESS 
NOCACHE
NOPARALLEL
MONITORING;


SET DEFINE OFF;
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('A', 40377, 4000, 4000, 13000, 
    0, 8340, 1660, 8000, 4400, 
    223380);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('B', 48, 50, 0, 160, 
    45, 50, 90, 70, 120, 
    2913);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('C', 3269, 7100, 6600, 24100, 
    9925, 0, 27975, 23700, 20000, 
    929437);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('D', 3330, 3000, 4400, 6100, 
    1700, 0, 10400, 3000, 18200, 
    628437);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('E', 13225, 3000, 0, 25100, 
    4500, 4500, 2900, 110, 14800, 
    171590);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('F', 1759, 526, 478, 0, 
    0, 0, 502, 484, 0, 
    13308);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('G', 2528, 0, 3500, 0, 
    3500, 0, 3000, 0, 0, 
    43706);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('H', 163, 950, 550, 2850, 
    1480, 735, 2025, 1450, 875, 
    52278);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('I', 158, 520, 200, 914, 
    350, 420, 540, 300, 150, 
    10557);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('J', 254, 750, 350, 1650, 
    1050, 570, 1300, 1070, 710, 
    25886);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('K', 75864, 20400, 0, 34800, 
    32400, 0, 32400, 28800, 34800, 
    701550);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('L', 18074, 15525, 0, 0, 
    41480, 27630, 0, 28850, 0, 
    861545);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('M', 45247, 20000, 0, 0, 
    35000, 60000, 0, 0, 0, 
    834240);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('N', 3332, 4125, 0, 5625, 
    2690, 3015, 2035, 3380, 5290, 
    152709);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('O', 287, 522, 132, 1878, 
    864, 66, 725, 795, 852, 
    36503);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('P', 338, 401, 134, 1113, 
    484, 127, 627, 745, 813, 
    18715);
Insert into AA_STK_AGING
   (ITEM, STOCK_AS_ON_DATE, A1TO90, A91TO120, A121TO270, 
    A271TO360, A361TO450, A451TO540, A541TO630, A631TO720, 
    A721PLUS)
 Values
   ('Q', 892, 650, 1000, 3200, 
    1800, 0, 3400, 3600, 2200, 
    113347);
COMMIT;

我们已经尝试使用 CASE 语句,但随着我们进一步移动到其他列,它变得越来越复杂。

CASE WHEN A1TO90 < STOCK_AS_ON_DATE THEN A1TO90 ELSE ( CASE WHEN A1TO90 >= STOCK_AS_ON_DATE THEN STOCK_AS_ON_DATE END ) END

一定有其他我们不知道的方法。请建议一种更简单的方法来实现我们的结果。提前致谢。

一个蛮力的方法可能是使用least()来决定是使用列数量还是将左边所有列加起来的余数;用 greatest() 停止任何负值。类似于:

select ITEM,
  STOCK_AS_ON_DATE,
  least(A1TO90,    STOCK_AS_ON_DATE) as A1TO90,
  least(A91TO120,  greatest(0, STOCK_AS_ON_DATE - A1TO90)) as A91TO120,
  least(A121TO270, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120)) as A121TO270,
  least(A271TO360, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270)) as A271TO360,
  least(A361TO450, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270 - A271TO360)) as A361TO450,
  least(A451TO540, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270 - A271TO360 - A361TO450)) as A451TO540,
  least(A541TO630, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270 - A271TO360 - A361TO450 - A451TO540)) as A541TO630,
  least(A631TO720, greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270 - A271TO360 - A361TO450 - A451TO540 - A541TO630)) as A631TO720,
  least(A721PLUS,  greatest(0, STOCK_AS_ON_DATE - A1TO90 - A91TO120 - A121TO270 - A271TO360 - A361TO450 - A451TO540 - A541TO630 - A631TO720)) as A721PLUS
from AA_STK_AGING;

这很丑陋并且不能很好地扩展,但本身并不复杂。 (这会在您用完库存后为列提供零;您的示例中项目 A 的最后一列为 null,但不确定它是否重要;使其为 null 只是稍微复杂一点。)

如果您想要显示的总列 - 再次不确定您是否真的这样做 - 您可以将其用作 CTE 或内联视图:

select t.*,
  A1TO90 + A91TO120 + A121TO270 + A271TO360 + A361TO450 + A451TO540 + A541TO630 + A631TO720 + A721PLUS as TOTAL
from (
  select ITEM,
    STOCK_AS_ON_DATE,
    least(A1TO90,    STOCK_AS_ON_DATE) as A1TO90,
    ...
  from AA_STK_AGING
) t;

db<>fiddle

如果您使用的是 11g,那么您可以将您的列取消透视为行:

select *
from AA_STK_AGING
unpivot (QUANTITY for BUCKET in (A1TO90 as 1, A91TO120 as 91, A121TO270 as 121, 
  A271TO360 as 271, A361TO450 as 361, A451TO540 as 451, A541TO630 as 541,
  A631TO720 as 631, A721PLUS as 721));

然后使用 运行 总数执行类似的计算:

select ITEM, STOCK_AS_ON_DATE, BUCKET,
  least(QUANTITY,
    greatest(0, STOCK_AS_ON_DATE + QUANTITY
      - sum(QUANTITY) over (partition by ITEM order by BUCKET))) as QUANTITY
from AA_STK_AGING
unpivot (QUANTITY for BUCKET in (A1TO90 as 1, A91TO120 as 91, A121TO270 as 121, 
    A271TO360 as 271, A361TO450 as 361, A451TO540 as 451, A541TO630 as 541,
    A631TO720 as 631, A721PLUS as 721)
);

并将其旋转回列:

select *
from (
  select ITEM, STOCK_AS_ON_DATE, BUCKET,
    least(QUANTITY,
      greatest(0, STOCK_AS_ON_DATE + QUANTITY
        - sum(QUANTITY) over (partition by ITEM order by BUCKET))) as QUANTITY
  from AA_STK_AGING
  unpivot (QUANTITY for BUCKET in (A1TO90 as 1, A91TO120 as 91, A121TO270 as 121, 
    A271TO360 as 271, A361TO450 as 361, A451TO540 as 451, A541TO630 as 541,
    A631TO720 as 631, A721PLUS as 721)
  )
)
pivot (max(quantity) for (bucket) in (1 as A1TO90, 91 as A91TO120, 121 as A121TO270, 
  271 as A271TO360, 361 as A361TO450, 451 as A451TO540, 541 as A541TO630,
  631 as A631TO720, 721 as A721PLUS)
)
order by ITEM;

db<>fiddle

如果您使用的是 10g,那么您可以手动取消旋转和旋转,但与蛮力方法相比,这可能不值得复杂化。

两种方法都给出:

ITEM STOCK_AS_ON_DATE A1TO90 A91TO120 A121TO270 A271TO360 A361TO450 A451TO540 A541TO630 A631TO720 A721PLUS TOTAL
---- ---------------- ------ -------- --------- --------- --------- --------- --------- --------- -------- -----
A               40377   4000     4000     13000         0      8340      1660      8000      1377        0 40377
B                  48     48        0         0         0         0         0         0         0        0    48
C                3269   3269        0         0         0         0         0         0         0        0  3269
D                3330   3000      330         0         0         0         0         0         0        0  3330
E               13225   3000        0     10225         0         0         0         0         0        0 13225
F                1759    526      478         0         0         0       502       253         0        0  1759
G                2528      0     2528         0         0         0         0         0         0        0  2528
H                 163    163        0         0         0         0         0         0         0        0   163
I                 158    158        0         0         0         0         0         0         0        0   158
J                 254    254        0         0         0         0         0         0         0        0   254
K               75864  20400        0     34800     20664         0         0         0         0        0 75864
L               18074  15525        0         0      2549         0         0         0         0        0 18074
M               45247  20000        0         0     25247         0         0         0         0        0 45247
N                3332   3332        0         0         0         0         0         0         0        0  3332
O                 287    287        0         0         0         0         0         0         0        0   287
P                 338    338        0         0         0         0         0         0         0        0   338
Q                 892    650      242         0         0         0         0         0         0        0   892

如果您使用的是 12c 或更高版本,您可以查看 match_recognize(),但您似乎使用的是 10g 或 11g。