【问题标题】:Recursive query design - Oracle SQL递归查询设计 - Oracle SQL
【发布时间】:2019-11-24 14:10:38
【问题描述】:

我希望创建一个能够将一系列补货完全映射到顶级需求的递归查询。

以下是有关我的问题陈述的一些详细信息:

我正在尝试设计一个递归解决方案来计算所有后续补货(及其水平)给出一个顶级需求,其中需求定义为生产或计划订单(MIN 类型)

我使用以下查询创建了这个SQLFIDDLE

-- schema
CREATE TABLE tblInputs (
    Type VARCHAR(256),
    Order_No VARCHAR(256),
    Planned_No VARCHAR(256),
    Purchase_No VARCHAR(256),
    Direction VARCHAR(256)
);

CREATE TABLE Requirements (
    Order_No VARCHAR(256),
    Planned_No VARCHAR(256),
    Req_ID VARCHAR(256),
    Req_No VARCHAR(256)
);

CREATE TABLE ReqRep (
    Req_ID VARCHAR(256),
    Req_No VARCHAR(256),
    Rep_ID VARCHAR(256)
);

CREATE TABLE Replenishments (
    Rep_ID VARCHAR(256),
    Order_No VARCHAR(256),
    Planned_No VARCHAR(256),
    Purchase_No VARCHAR(256)
);


-- data
INSERT INTO tblInputs (Type, Order_No, Planned_No, Purchase_No, Direction)

SELECT 'Purchase', NULL, NULL, 'PO9000124798', 'PLUS' FROM DUAL
UNION ALL
SELECT 'Planned', NULL, 'PL191908851', NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Planned', NULL, 'PL191908852', NULL, 'PLUS' FROM DUAL 
UNION ALL
SELECT 'Planned', NULL, 'PL191908853', NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Planned', NULL, 'PL191908854', NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Planned', NULL, 'PL191908855', NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Planned', NULL, 'PL191908853', NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Build', 'O103916639', NULL, NULL, 'MIN' FROM DUAL
UNION ALL
SELECT 'Production', 'O103962037', NULL, NULL, 'PLUS' FROM DUAL
UNION ALL
SELECT 'Production', 'O103933200', NULL, NULL, 'PLUS' FROM DUAL
;

INSERT INTO Requirements (Order_No, Planned_No, Req_ID, Req_No)
SELECT NULL, 'PL191908851', 'ABA', '36' FROM DUAL UNION ALL
SELECT NULL, 'PL191908852', 'CC',  '93' FROM DUAL UNION ALL
SELECT NULL, 'PL191908853', 'BBA', '27' FROM DUAL UNION ALL
SELECT NULL, 'PL191908854', 'EWE', '42' FROM DUAL UNION ALL
SELECT NULL, 'PL191908855', 'WWW', '13' FROM DUAL UNION ALL
SELECT NULL, 'PL191908856', 'EEE', '33' FROM DUAL UNION ALL
SELECT NULL, 'PL191909922', 'GFW', '99' FROM DUAL UNION ALL
SELECT NULL, 'PL191910022', 'GFT', '23' FROM DUAL UNION ALL
SELECT NULL, 'PL192010120', 'THE', '54' FROM DUAL UNION ALL
SELECT 'O103962037',  NULL, 'BDD', '37' FROM DUAL UNION ALL
SELECT 'O103933200',  NULL, 'DFA', '63' FROM DUAL UNION ALL
SELECT 'O103547812',  NULL, 'ADS', '45' FROM DUAL UNION ALL
SELECT 'O103547415',  NULL, 'DWQ', '94' FROM DUAL UNION ALL
SELECT 'O103654787',  NULL, 'QZX', '96' FROM DUAL UNION ALL
SELECT 'O103214217',  NULL, 'NFD', '20' FROM DUAL UNION ALL
SELECT 'O103215320',  NULL, 'GBV', '33' FROM DUAL UNION ALL
SELECT 'O106212219',  NULL, 'ERQ', '22' FROM DUAL UNION ALL
SELECT 'O103215320',  NULL, 'MRP', '11' FROM DUAL
;

INSERT INTO ReqRep (Req_ID, Req_No, Rep_ID)
SELECT 'ABA', '36', '7736' FROM DUAL UNION ALL
SELECT 'CCC', '93', '6686' FROM DUAL UNION ALL
SELECT 'BBA', '27', '5710' FROM DUAL UNION ALL
SELECT 'EWE', '42', '7634' FROM DUAL UNION ALL
SELECT 'WWW', '13', '9393' FROM DUAL UNION ALL
SELECT 'EEE', '33', '8442' FROM DUAL UNION ALL
SELECT 'GFW', '99', '5758' FROM DUAL UNION ALL
SELECT 'GFT', '23', '5988' FROM DUAL UNION ALL
SELECT 'THE', '54', '6748' FROM DUAL UNION ALL
SELECT 'BDD', '37', '7123' FROM DUAL UNION ALL
SELECT 'BDD', '37', '7124' FROM DUAL UNION ALL
SELECT 'BDD', '37', '7125' FROM DUAL UNION ALL
SELECT 'BDD', '37', '7126' FROM DUAL UNION ALL
SELECT 'DFA', '63', '7125' FROM DUAL UNION ALL
SELECT 'ADS', '45', '5855' FROM DUAL UNION ALL
SELECT 'DWQ', '80', '9419' FROM DUAL UNION ALL
SELECT 'QZX', '96', '5748' FROM DUAL UNION ALL
SELECT 'NFD', '20', '7055' FROM DUAL UNION ALL
SELECT 'ERQ', '22', '7736' FROM DUAL UNION ALL
SELECT 'MRP', '11', '7736' FROM DUAL UNION ALL
SELECT 'GBV', '33', '9999' FROM DUAL
;

INSERT INTO Replenishments(Rep_ID, Order_No, Planned_No, Purchase_No)
SELECT '7736', NULL, NULL, 'PO9000124799' FROM DUAL UNION ALL
SELECT '6686', NULL, NULL, 'PO9000124800' FROM DUAL UNION ALL
SELECT '5710', NULL, NULL, 'PO9000124801' FROM DUAL UNION ALL
SELECT '7634', NULL, NULL, 'PO9000124802' FROM DUAL UNION ALL
SELECT '9393', NULL, NULL, 'PO9000124803' FROM DUAL UNION ALL
SELECT '8442', NULL, NULL, 'PO9000124804' FROM DUAL UNION ALL
SELECT '5758', NULL, NULL, 'PO9000124805' FROM DUAL UNION ALL
SELECT '5988', NULL, NULL, 'PO9000124806' FROM DUAL UNION ALL
SELECT '6748', NULL, NULL, 'PO9000124807' FROM DUAL UNION ALL
SELECT '7123', 'O103654787', NULL, NULL FROM DUAL UNION ALL
SELECT '7124', 'O103214217', NULL, NULL FROM DUAL UNION ALL
SELECT '7125', 'O103215320', NULL, NULL FROM DUAL UNION ALL
SELECT '7126', 'O106212219', NULL, NULL FROM DUAL UNION ALL
SELECT '7125', 'O103215320', NULL, NULL FROM DUAL UNION ALL
SELECT '5855', NULL, 'PL192010120', NULL FROM DUAL UNION ALL
SELECT '9419', NULL, 'PL121122221', NULL FROM DUAL UNION ALL
SELECT '5748', NULL, 'PL272634123', NULL FROM DUAL UNION ALL
SELECT '7055', NULL, 'PL983002032', NULL FROM DUAL UNION ALL
SELECT '9999', NULL, NULL, 'PO9000124806' FROM DUAL UNION ALL
SELECT '1111', NULL, NULL, 'PO9000124806' FROM DUAL
;

对我的SQLFIDDLE 使用以下查询,我基本上可以获得第一个“级别”:

WITH

  -- Use one order as an example
  -- This will eventually be used for many orders at once
  currOrder AS
    (
      SELECT
        tblInputs.Order_No
      FROM
        tblInputs
      WHERE
        Direction = 'PLUS'
        AND
        (Type = 'Production'  OR Type = 'Planned')
        -- Added this for example purposes only!
        AND
        Order_No LIKE '%O103962037%'
     ),

    -- Now get the details about this order being a REQUIREMENT
   Req AS
     (
       SELECT
        -- unique identifier(s), unfortunately two fields combine to make the primary key
         Req_ID,
         Req_No

       FROM
         Requirements
           INNER JOIN currOrder ON currOrder.Order_No = Requirements.Order_No
      ),

   -- This is a giant bridge table of requirements, and replenishments
   -- A requirement can be made up of many replenishments, and a replenishment can satisfy many requirements
   -- Find the replenishments for our specific requirement
   Req_Rep AS
     (
       SELECT DISTINCT
         ReqRep.REP_ID

       FROM
         ReqRep
           INNER JOIN Req ON ReqRep.Req_ID = Req.Req_ID AND ReqRep.Req_No = Req.Req_No
      ),

  -- Now, grab the replenishment data that we care about, and its details
  -- Only one field will be filled in at a time depending on what the replenishment type is
   Rep AS
     (
       SELECT DISTINCT
         Req_Rep.Rep_ID,
         CASE
           WHEN Replenishments.Order_No IS NULL AND Replenishments.Planned_No IS NULL THEN Replenishments.Purchase_No
           WHEN Replenishments.Order_No IS NULL AND Replenishments.Planned_No IS NOT NULL AND Replenishments.Purchase_No IS NULL THEN Replenishments.Planned_No
           WHEN Replenishments.Order_No IS NOT NULL AND Replenishments.Planned_No IS NULL AND Replenishments.Purchase_No IS NULL THEN Replenishments.Order_No
         ELSE NULL END AS The_Number

        FROM
         Req_Rep
           INNER JOIN Replenishments on Replenishments.Rep_ID = Req_Rep.Rep_ID
      )

   -- Grab the results
   SELECT DISTINCT * FROM Rep 

使用该查询,我得到了这个数据集:

REP_ID  THE_NUMBER
7123    O103654787
7125    O103215320
7124    O103214217
7126    O106212219

这向我表明,对于特定的要求(由其Order_No 指定),这些是满足它的补充。然后,对于那些补货,这是与它们相关联的元数据。

我现在需要采取The_Number 并基本上重复整个过程。并且对于作为生产订单或计划订单的每个补货,继续查找所有的补货(因为,如果补货是生产订单或计划订单,它可能会有额外的补货)

目标数据集如下所示:

|    Number   | Req_ID | Req_No | Rep_ID |    Details   | Level |
|:-----------:|:------:|:------:|:------:|:------------:|:-----:|
|  O103962037 |   BDD  |   377  |  7123  |  O103654787  |   1   |
|  O103962037 |   BDD  |   377  |  7124  |  O103214217  |   1   |
|  O103962037 |   BDD  |   377  |  7125  |  O103215320  |   1   |
|  O103962037 |   BDD  |   377  |  7126  |  O106212219  |   1   |
|  O103654787 |   QZX  |   96   |  5748  |  PL272634123 |   2   |
|  O103214217 |   NFD  |   20   |  7055  |  PL983002032 |   2   |
|  O103215320 |   GBV  |   33   |  9999  | PO9000124806 |   2   |
|  O106212219 |   ERQ  |   22   |  7736  | PO9000124799 |   2   |
| PL272634123 |   MRP  |   99   |  1111  | PO9000124806 |   3   |
| PL983002032 |   EWE  |   22   |  1111  | PO9000124806 |   3   |

我相信我已经在查询中实现了递归anchor,但我无法确定如何构建其余部分。任何建议表示赞赏。

我已经查看了这些帖子:

我认为我需要的行是Rep.The_Number = Requirements.Order_No,但我不知道如何定义它。

【问题讨论】:

  • 是否有 7125 Rep_ID 在 Replenishments 表中有重复的原因,或者这是一个错误?
  • 这是一个错误。很棒的收获。抱歉@PatrickH,我已将其删除

标签: sql oracle recursion oracle11g


【解决方案1】:

假设我正确理解了数据模型,我想出了一个解决方案,我希望它至少可以作为您更复杂版本的起点。 SQLFiddle 在这里。我不得不修改样本数据,因为我无法根据提供的样本复制您的预期结果。

我想出的SQL是这样的:

WITH t as (
SELECT coalesce(r.planned_no, r.Order_No) order_no,
       r.Req_ID,
       r.Req_No,
       rr.REP_ID,
       coalesce(rp.purchase_no, rp.planned_no, rp.order_no) details,
       0 replevel
  FROM requirements r
  JOIN ReqRep rr ON rr.Req_ID = r.Req_ID AND rr.Req_No = r.Req_No
  JOIN Replenishments rp on rp.Rep_ID = rr.Rep_ID),
det_t  (order_no, req_id, req_no, rep_id, details, replevel, rep_path) as (
SELECT t.Order_No,
       t.Req_ID,
       t.Req_No,
       t.REP_ID,
       t.details,
       1 replevel,
       t.order_no rep_path
  FROM t
 WHERE Order_No LIKE '%O103962037%'
 UNION ALL
 SELECT t.Order_No,
       t.Req_ID,
       t.Req_No,
       t.REP_ID,
       t.details,
       d.replevel + 1,
       rep_path || '=>' || t.order_no rep_path
  FROM det_t d
  JOIN t ON t.order_no = d.details
)
select order_no,
       req_id,
       req_no,
       rep_id,
       details,
       replevel,
       rep_path
  from det_t
 order by replevel, order_no;

示例执行如下:

FSITJA@db01 2019-07-16 09:59:00> WITH t as (
  2  SELECT coalesce(r.planned_no, r.Order_No) order_no,
  3         r.Req_ID,
  4         r.Req_No,
  5         rr.REP_ID,
  6         coalesce(rp.purchase_no, rp.planned_no, rp.order_no) details,
  7         0 replevel
  8    FROM requirements r
  9    JOIN ReqRep rr ON rr.Req_ID = r.Req_ID AND rr.Req_No = r.Req_No
 10    JOIN Replenishments rp on rp.Rep_ID = rr.Rep_ID),
 11  det_t  (order_no, req_id, req_no, rep_id, details, replevel, rep_path) as (
 12  SELECT t.Order_No,
 13         t.Req_ID,
 14         t.Req_No,
 15         t.REP_ID,
 16         t.details,
 17         1 replevel,
 18         t.order_no rep_path
 19    FROM t
 20   WHERE Order_No LIKE '%O103962037%'
 21   UNION ALL
 22   SELECT t.Order_No,
 23         t.Req_ID,
 24         t.Req_No,
 25         t.REP_ID,
 26         t.details,
 27         d.replevel + 1,
 28         rep_path || '=>' || t.order_no rep_path
 29    FROM det_t d
 30    JOIN t ON t.order_no = d.details
 31  )
 32  select order_no,
 33         req_id,
 34         req_no,
 35         rep_id,
 36         details,
 37         replevel,
 38         rep_path
 39    from det_t
 40   order by replevel, order_no;

ORDER_NO    REQ_ID  REQ_NO  REP_ID  DETAILS           REPLEVEL REP_PATH
----------- ------- ------- ------- --------------- ---------- -------------------------------------
O103962037  BDD     37      7123    O103654787               1 O103962037
O103962037  BDD     37      7124    O103214217               1 O103962037
O103962037  BDD     37      7125    O103215320               1 O103962037
O103962037  BDD     37      7126    O106212219               1 O103962037
O103214217  NFD     20      7055    PL191908854              2 O103962037=>O103214217
O103215320  GBV     33      9999    PO9000124806             2 O103962037=>O103215320
O103215320  MRP     11      7736    PO9000124799             2 O103962037=>O103215320
O103654787  QZX     96      5748    PL272634123              2 O103962037=>O103654787
O106212219  ERQ     22      7736    PO9000124799             2 O103962037=>O106212219
PL191908854 EWE     42      7634    PO9000124802             3 O103962037=>O103214217=>PL191908854
PL272634123 XYZ     36      8888    PL2222222222             3 O103962037=>O103654787=>PL272634123

11 rows selected.

我认为样本数据与预期结果不匹配。 根据您的预期结果,除非我误解了数据模型,否则第 3 级行(MRP 和 EWE)REQ_NO 与示例数据中的内容不匹配。

MRP 在预期结果中的 Req_No = 99,在插入中它的 Req_No = 11,将其连接到 Replenishment level 1 Rep_Id = 7125 下的 level 2。

EWE 在预期结果中具有 Req_No = 22,在插入中具有 Req_No = 42,并且它从 Replenishment Rep_Id = 7055 下的级别 1 连接。 但是,在表 Requirements EWE 中有 Planned_No = 'PL191908854' 而在 2 级补货 Rep_Id = 7055 指向 Order_No = 'PL983002032'。

如果我将提供的关于 Replinishment Rep_Id = 7055 的示例数据从 Order_No = 'PL983002032' 更改为 'PL191908854',它会在 3 级下正确显示 EWE。

这是我在 Replenishment table insert 中更改的内容(反映在 SQLFiddle 中): 添加(只是为了说明有效的 3 级连接):

SELECT '8888', NULL, NULL, 'PL2222222222' FROM DUAL

修改(让 EWE 正确显示在第 3 级):

SELECT '7055', NULL, 'PL191908854', NULL FROM DUAL UNION ALL

【讨论】:

  • 这是一个好的开始,谢谢。但正如您所指出的,这不适用于我发布的数据,这是我正在使用的实际数据。您能否详细说明您的答案,让我/其他观众更好地了解您所做的更改以及它是如何工作的?
  • 我将编辑我的答案,因为在 cmets 中没有足够的空间输入解释。
  • 我可以稍微编辑一下,让它正常工作。谢谢。
【解决方案2】:

这是我能想到的。这与您对该给定订单的预期结果非常接近,只是您的需求表中有 O103215320 的重复项,这可能是您的数据错误。

我对@9​​87654323@ 表如何适应这个有点困惑。我下面的查询根本没有使用该表。

with orders as (
select nvl(Requirements.order_no,Requirements.Planned_No) order_no,
       Requirements.Req_ID,
       Requirements.Req_No,
       Replenishments.Rep_ID,
       coalesce(Replenishments.Order_No,Replenishments.Planned_No,Replenishments.Purchase_No) Details
from Requirements
join ReqRep
  on ReqRep.Req_ID = Requirements.Req_ID
 and ReqRep.Req_No = Requirements.Req_No
join Replenishments
  on Replenishments.Rep_ID = ReqRep.Rep_ID)

select order_no,
       Req_ID,
       Req_No,
       Rep_ID,
       Details,
       lvl
from (
select orders.*,
       connect_by_root orders.Order_No root_order,
       level lvl
from orders
connect by prior orders.details = orders.order_no)
where root_order like '%O103962037%'
order by lvl,order_no

上述查询给出以下结果:

+-------------+--------+--------+--------+--------------+-----+
|  ORDER_NO   | REQ_ID | REQ_NO | REP_ID |   DETAILS    | LVL |
+-------------+--------+--------+--------+--------------+-----+
| O103962037  | BDD    |     37 |   7123 | O103654787   |   1 |
| O103962037  | BDD    |     37 |   7125 | O103215320   |   1 |
| O103962037  | BDD    |     37 |   7124 | O103214217   |   1 |
| O103962037  | BDD    |     37 |   7126 | O106212219   |   1 |
| O103214217  | NFD    |     20 |   7055 | PL983002032  |   2 |
| O103215320  | MRP    |     11 |   7736 | PO9000124799 |   2 |
| O103215320  | GBV    |     33 |   9999 | PO9000124806 |   2 |
| O103654787  | QZX    |     96 |   5748 | PL272634123  |   2 |
| O106212219  | ERQ    |     22 |   7736 | PO9000124799 |   2 |
| PL272634123 | MRP    |     99 |   1111 | PO9000124806 |   3 |
| PL983002032 | EWE    |     22 |   1111 | PO9000124806 |   3 |
+-------------+--------+--------+--------+--------------+-----+

【讨论】:

  • 帕特里克,这太棒了。不幸的是,我在发布之前已经尝试过其他解决方案,但这很有趣。我会更多地阅读connect_by_prior
  • 您可以阅读更多关于它的信息here
猜你喜欢
  • 2020-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-14
  • 2013-03-01
  • 2011-07-19
  • 1970-01-01
相关资源
最近更新 更多