Примечание: если у вас возникают проблемы с запуском MATCH_RECOGNIZE
, это может быть связано с тем, что вы используете (не слишком) старую версию SQL * Developer. Попробуйте последнюю версию или используйте взамен SQL * Navigator, TOAD или SQL * Plus. Проблема в "?" символ, который сбивает с толку SQL * Developer, поскольку это символ, который JDBC использует для переменных связывания.
У вас проблема с моделью данных. А именно, дочерние записи в вашей таблице prod_conf_cost_struct_cvl
не явно связаны с их родительскими строками. Вот почему сборка «DEF» вызывает проблемы. Без явной связи невозможно точно рассчитать данные.
Вы должны исправить эту модель данных и добавить parent_sequence_no
к каждой записи, чтобы (например) вы могли сказать, что sequence_no
80 - это дочерний элемент sequence_no
70, а не дочерний элемент sequence_no
20.
Однако, поскольку я не могу предположить, что у вас есть время или полномочия для изменения вашей модели данных, я отвечу на вопрос с моделью данных как есть.
Прежде всего, давайте добавим QTY
и PURCH_CURR
к вашим образцам данных.
with prod_conf_cost_struct_clv ( model_no, revision, sequence_no, part_no, component_part, lvl, cost, qty, purch_curr ) as
(
SELECT 62, 1, 00, 'XXX', 'ABC', 1, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 10, 'ABC', '123', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 20, '123', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 30, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 40, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 50, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 60, 'ABC', '356', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 70, '356', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 80, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 90, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 100, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL )
select * from prod_conf_cost_struct_clv;
+----------+----------+-------------+---------+----------------+-----+------+-----+------------+
| MODEL_NO | REVISION | SEQUENCE_NO | PART_NO | COMPONENT_PART | LVL | COST | QTY | PURCH_CURR |
+----------+----------+-------------+---------+----------------+-----+------+-----+------------+
| 62 | 1 | 0 | XXX | ABC | 1 | | 1 | GBP |
| 62 | 1 | 10 | ABC | 123 | 2 | | 1 | GBP |
| 62 | 1 | 20 | 123 | DEF | 3 | | 1 | GBP |
| 62 | 1 | 30 | DEF | 456 | 4 | 100 | 1 | GBP |
| 62 | 1 | 40 | DEF | 789 | 4 | 50 | 1 | GBP |
| 62 | 1 | 50 | DEF | 024 | 4 | 20 | 1 | GBP |
| 62 | 1 | 60 | ABC | 356 | 2 | | 1 | GBP |
| 62 | 1 | 70 | 356 | DEF | 3 | | 1 | GBP |
| 62 | 1 | 80 | DEF | 456 | 4 | 100 | 1 | GBP |
| 62 | 1 | 90 | DEF | 789 | 4 | 50 | 1 | GBP |
| 62 | 1 | 100 | DEF | 024 | 4 | 20 | 1 | GBP |
+----------+----------+-------------+---------+----------------+-----+------+-----+------------+
ПРИМЕЧАНИЕ: вы не показываете, как несколько валют будут представлены в ваших тестовых данных, поэтому моя обработка этого вопроса в этом ответе может быть неправильной.
ОК, поэтому первое, что нам нужно сделать, это выяснить значение parent_sequence_no
(которое действительно должно быть в вашей таблице - см. Выше). Поскольку его нет в вашей таблице, нам нужно его вычислить. Мы вычислим его как sequence_no
строки, имеющей наибольшее значение sequence_no
, которое меньше текущей строки, и значение level
(которое я назвал lvl
, чтобы избежать использования ключевого слова Oracle) на единицу меньше текущая строка.
Чтобы найти это значение эффективно, мы можем использовать функциональность MATCH_RECOGNIZE
, чтобы описать, как должна выглядеть родительская строка для каждого дочернего элемента.
Мы назовем набор результатов с этим новым parent_sequence_no
столбцом corrected_hierarchy
.
with prod_conf_cost_struct_clv ( model_no, revision, sequence_no, part_no, component_part, lvl, cost, qty, purch_curr ) as
(
SELECT 62, 1, 00, 'XXX', 'ABC', 1, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 10, 'ABC', '123', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 20, '123', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 30, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 40, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 50, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 60, 'ABC', '356', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 70, '356', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 80, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 90, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 100, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL )
-- Step 1: correct for your data model problem, which is the fact that child rows
-- (e.g., operations 30-50) are not *explicitly* linked to their parent rows (e.g.,
-- operation 20)
, corrected_hierarchy ( model_no, revision, parent_sequence_no, sequence_no, part_no, component_part, lvl, cost, qty, purch_curr ) AS
(
SELECT *
FROM prod_conf_cost_struct_clv c
MATCH_RECOGNIZE (
PARTITION BY model_no, revision
ORDER BY sequence_no desc
MEASURES (P.sequence_no) AS parent_sequence_no,
c.sequence_no AS sequence_no, c.part_no as part_no, c.component_part as component_part, c.lvl as lvl, c.cost as cost, c.qty as qty, c.purch_curr as purch_curr
ONE ROW PER MATCH
AFTER MATCH SKIP TO NEXT ROW
-- C => child row
-- S* => zero or more siblings or children of siblings that might be
-- between child and its parent
-- P? => parent row, which may not exist (e.g., for the root operation)
PATTERN (C S* P?)
DEFINE
C AS 1=1,
S AS S.lvl >= C.lvl,
P AS P.lvl = C.lvl - 1 AND P.component_part = C.part_no
)
ORDER BY model_no, revision, sequence_no )
SELECT * FROM corrected_hierarchy;
+----------+----------+--------------------+-------------+---------+----------------+-----+------+-----+------------+
| MODEL_NO | REVISION | PARENT_SEQUENCE_NO | SEQUENCE_NO | PART_NO | COMPONENT_PART | LVL | COST | QTY | PURCH_CURR |
+----------+----------+--------------------+-------------+---------+----------------+-----+------+-----+------------+
| 62 | 1 | | 0 | XXX | ABC | 1 | | 1 | GBP |
| 62 | 1 | 0 | 10 | ABC | 123 | 2 | | 1 | GBP |
| 62 | 1 | 10 | 20 | 123 | DEF | 3 | | 1 | GBP |
| 62 | 1 | 20 | 30 | DEF | 456 | 4 | 100 | 1 | GBP |
| 62 | 1 | 20 | 40 | DEF | 789 | 4 | 50 | 1 | GBP |
| 62 | 1 | 20 | 50 | DEF | 024 | 4 | 20 | 1 | GBP |
| 62 | 1 | 0 | 60 | ABC | 356 | 2 | | 1 | GBP |
| 62 | 1 | 60 | 70 | 356 | DEF | 3 | | 1 | GBP |
| 62 | 1 | 70 | 80 | DEF | 456 | 4 | 100 | 1 | GBP |
| 62 | 1 | 70 | 90 | DEF | 789 | 4 | 50 | 1 | GBP |
| 62 | 1 | 70 | 100 | DEF | 024 | 4 | 20 | 1 | GBP |
+----------+----------+--------------------+-------------+---------+----------------+-----+------+-----+------------+
Теперь вы можете остановиться прямо здесь, если хотите. Все, что вам нужно сделать, это использовать логику corrected_hierarchy
в вашей функции calc_cost
, заменив
and part_no in (
select component_part
...
с
and parent_sequence_no = sequence_no_
Но, как указал @Def, вам действительно не нужна функция PL / SQL для того, что вы пытаетесь сделать.
То, что вы, похоже, пытаетесь сделать, - это распечатать иерархическую ведомость материалов, в которой стоимость уровня каждого элемента (стоимость уровня равна стоимости прямого и косвенного подкомпонентов элемента).
Вот запрос, который делает это, собирая все вместе:
with prod_conf_cost_struct_clv ( model_no, revision, sequence_no, part_no, component_part, lvl, cost, qty, purch_curr ) as
(
SELECT 62, 1, 00, 'XXX', 'ABC', 1, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 10, 'ABC', '123', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 20, '123', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 30, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 40, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 50, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 60, 'ABC', '356', 2, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 70, '356', 'DEF', 3, null, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 80, 'DEF', '456', 4, 100, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 90, 'DEF', '789', 4, 50, 1, 'GBP' FROM DUAL UNION ALL
SELECT 62, 1, 100, 'DEF', '024', 4, 20, 1, 'GBP' FROM DUAL )
-- Step 1: correct for your data model problem, which is the fact that child rows
-- (e.g., operations 30-50) are not *explicitly* linked to their parent rows (e.g.,
-- operation 20)
, corrected_hierarchy ( model_no, revision, parent_sequence_no, sequence_no, part_no, component_part, lvl, cost, qty, purch_curr ) AS
(
SELECT *
FROM prod_conf_cost_struct_clv c
MATCH_RECOGNIZE (
PARTITION BY model_no, revision
ORDER BY sequence_no desc
MEASURES (P.sequence_no) AS parent_sequence_no,
c.sequence_no AS sequence_no, c.part_no as part_no, c.component_part as component_part, c.lvl as lvl, c.cost as cost, c.qty as qty, c.purch_curr as purch_curr
ONE ROW PER MATCH
AFTER MATCH SKIP TO NEXT ROW
PATTERN (C S* P?)
DEFINE
C AS 1=1,
S AS S.lvl >= C.lvl,
P AS P.lvl = C.lvl - 1 AND P.component_part = C.part_no
)
ORDER BY model_no, revision, sequence_no ),
sequence_hierarchy_costs as (
SELECT model_no,
revision,
min(sequence_no) sequence_no,
purch_curr,
sum(h.qty * h.cost) hierarchy_cost
FROM corrected_hierarchy h
WHERE 1=1
connect by model_no = prior model_no
and revision = prior revision
and parent_sequence_no = prior sequence_no
group by model_no, revision, connect_by_root sequence_no, purch_curr )
SELECT level,
sys_connect_by_path(h.sequence_no, '->') path,
shc.hierarchy_cost
FROM corrected_hierarchy h
INNER JOIN sequence_hierarchy_costs shc ON shc.model_no = h.model_no and shc.revision = h.revision and shc.sequence_no = h.sequence_no and shc.purch_curr = h.purch_curr
WHERE h.model_no = 62
and h.revision = 1
START WITH h.sequence_no = 20
connect by h.model_no = prior h.model_no
and h.revision = prior h.revision
and h.parent_sequence_no = prior h.sequence_no;
+-------+----------+----------------+
| LEVEL | PATH | HIERARCHY_COST |
+-------+----------+----------------+
| 1 | ->20 | 170 |
| 2 | ->20->30 | 100 |
| 2 | ->20->40 | 50 |
| 2 | ->20->50 | 20 |
+-------+----------+----------------+
Вы можете видеть, что было бы намного проще, если бы parent_sequence_no
были в вашей модели данных для начала.