Oracle sql 拆分行以用参考文章填充最大数量

Oracle sql split up rows to fill maxquantity with reference articles

这是一个扩展问题

假设我有一个文章列表,我想将其拆分以填充最大值包括附加文章(第 7 条),它引用其他位置:

id | ref  | name  | quantity | maxquantity
1  | null | name_a|        3 |           5
2  | null | name_a|        1 |           5
3  | null | name_a|        3 |           5
4  | null | name_a|        5 |           5
5  | null | name_b|        7 |           4
6  | null | name_b|        2 |           4
7  |    5 | add_1 |       14 |        null

我想创建按名称分组的包,填充到最大值,保持引用关系和引用文章与引用文章的比率以获得以下内容结果:

1  | null | name_a|        3 |           5 | name_a_part1 |                  3
2  | null | name_a|        1 |           5 | name_a_part1 |                  1
3  | null | name_a|        3 |           5 | name_a_part1 |                  1
                                                                        ^- sum() = maxquantity

3  | null | name_a|        3 |           5 | name_a_part2 |                  2
4  | null | name_a|        5 |           5 | name_a_part2 |                  3
                                                                        ^- sum() = maxquantity

4  | null | name_a|        5 |           5 | name_a_part3 |                  2
                                                                        ^- sum() = maxquantity or the rest of name_a

5  | null | name_b|        7 |           4 | name_b_part1 |                  4
                                                                        ^- sum() = maxquantity

5  | null | name_b|        7 |           4 | name_b_part2 |                  3
6  | null | name_b|        2 |           4 | name_b_part2 |                  1
                                                                        ^- sum() = maxquantity

6  | null | name_b|        2 |           4 | name_b_part3 |                  1
                                                                        ^- sum() = maxquantity or the rest of name_b

7  |    5 |  add_1|        14|        null | name_b_part1 |                  8
7  |    5 |  add_1|        14|        null | name_b_part2 |                  6

pos5 与 pos7 的比率是 1:2

最终 bin 的名称或编号应在 referenced-articles 和 referencing-articles 之间匹配

我设法解决了这个问题。

通过

创建table
CREATE TABLE articles (pos, ref_pos, article, quantity, maxquantity ) AS
SELECT 0, NULL, 'prod1', 3, 6 FROM DUAL UNION ALL
SELECT 1, NULL, 'prod1', 3, 6 FROM DUAL UNION ALL
SELECT 2, NULL, 'prod1', 8, 6 FROM DUAL UNION ALL
SELECT 7, 2, 'addon_for_pos2', 16, NULL FROM DUAL 

这 sql 将得到正确的结果:

WITH split_bins (pos, ref_pos, article, quantity, maxquantity, bin_tag, bin_tag2, effective_quantity, prev_quantity,effective_name, ratio) AS (
-- ################### the first static iteration    
  SELECT pos,
         ref_pos,
         article,
         quantity,
-- ################### calculate the max-quantity
         COALESCE(
           maxquantity, CONNECT_BY_ROOT maxquantity * quantity / CONNECT_BY_ROOT quantity
        ) AS maxquantity,
-- ################### calculate the bin_tag for grouping
         FLOOR(
           COALESCE(
             SUM(quantity) OVER (
               PARTITION BY article
               ORDER BY pos
               ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
             ),
             0
           )
           / COALESCE(
               maxquantity, CONNECT_BY_ROOT maxquantity * quantity / CONNECT_BY_ROOT quantity
             )
         ) + 1 as bin_tag,
-- ################### calculate the bin_tag for grouping supplements to correct bin
         FLOOR(
           COALESCE(
             SUM(quantity) OVER (
               PARTITION BY article, pos
               ORDER BY pos
               ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
             ),
             0
           )
           / COALESCE(
               maxquantity, CONNECT_BY_ROOT maxquantity * quantity / CONNECT_BY_ROOT quantity
             )
         ) + 1 as bin_tag2,
-- ################### calculate the effective quantity
         LEAST(
            COALESCE(
              maxquantity, CONNECT_BY_ROOT maxquantity * quantity / CONNECT_BY_ROOT quantity
            )
           - MOD(
               COALESCE(
                 SUM(quantity) OVER (
                   PARTITION BY article
                   ORDER BY pos
                   ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
                 ),
                 0
               ),
               COALESCE(
          maxquantity, CONNECT_BY_ROOT maxquantity * quantity / CONNECT_BY_ROOT quantity
        )
             ),
           quantity
         ) AS effective_quantity,
-- ################### previously used quantity (start with zero)      
         0  AS prev_quantity,
-- ################### propagate the referenced article to the referencing articles    
    CONNECT_BY_ROOT article AS effective_name, 
-- ################### calculate the ratio of main articles and addons (just dev)    
    quantity / CONNECT_BY_ROOT quantity AS ratio 
  FROM 
    articles START WITH ref_pos IS NULL CONNECT BY PRIOR pos = ref_pos
-- ################### the 2nd to n iteration    
UNION ALL
--(pos, ref_pos, article, quantity, maxquantity, bin_tag, effective_quantity, prev_quantity,effective_name, ratio)
  SELECT pos,
         ref_pos,
         article,
         quantity,
         maxquantity,
-- ################### increase the identifier    
         bin_tag + 1 as bin_tag,
         bin_tag2 + 1 as bin_tag2,
-- ################### calculate the current effective_quantity    
         LEAST(
           quantity - prev_quantity - effective_quantity,
           maxquantity
         ) as effective_quantity,
-- ################### calculate the prev_quantity for next iteration    
         prev_quantity + effective_quantity as prev_quantity,
         effective_name, 
         ratio 
  FROM   split_bins
  WHERE  prev_quantity + effective_quantity < quantity
  )
-- ################### final select data from with-clause    
SELECT pos, ref_pos, article, quantity, maxquantity, bin_tag, bin_tag2,effective_quantity, prev_quantity,effective_name, ratio,effective_name||'_limit_'||connect_by_root bin_tag as id
FROM   split_bins START WITH ref_pos IS NULL CONNECT BY PRIOR pos = ref_pos and PRIOR bin_tag2=bin_tag2
order by pos, bin_tag;

fiddle