2011-01-24 3 views
2

У меня есть интересная проблема SQL. У меня есть иерархическая таблица частей, которые составляют спецификацию. похожее на это:Помогите рассчитать сложную сумму в иерархическом наборе данных

ASSEMBLY 
--------- 
parent_part_id 
part_id 
quantity 

Я получаю иерархию этой структуры с помощью запроса, как это:

SELECT level, part_id, quantity 
from assembly 
start with parent_part_id = 1 
connect by parent_part_id = prior part_id; 

вывод может выглядеть следующим образом:

level part_id quantity 
----- ------- --------- 
1  2  2 
2  3  10 
1  4  2 
2  5  1  
3  3  5 

до сих пор так хорошо.

вопрос в следующем: как рассчитать общее количество каждой детали, необходимой для сборки верхнего уровня (часть 1)?

Группировка этого результата по частям и суммирование количества неверны, так как количество должно быть умножено на количество части, непосредственно над текущей частью иерархии, рекурсивно вверх по дереву.

Я думаю, что это функция LAG, но у нее проблемы с ее визуализацией.

редактировать: Ожидаемые результаты:

part_id quantity 
------- -------- 
2  2 
3  30 
4  2 
5  2 
более

редактировать: я получить интересные результаты с этим запросом

SELECT rownum, level lvl, part_id, quantity, unit_of_measure 
       , connect_by_isleaf || sys_connect_by_path(quantity,'*') math 
      from assembly 
      start with parent_part_id = 1 
      connect by parent_part_id = prior part_id 

столбец математики возвращает строковое представление расчета я хочу выполнить :) для например, можно сказать:

1*1*2*10 

или что-то подобное и подходящее ... возможно, создавая функцию для синтаксического анализа этого и возврата результата, решит проблему. Кто-нибудь считает, что это возмутительно?

+0

Мне очень жаль, но я не понимаю, вопрос * Как рассчитать общее количество каждой необходимой для того, чтобы сделать сборку верхнего уровня (часть 1) * и на соответствующую записку , как вы получаете ожидаемый результат. –

+0

@Rene - часть 1 является исходной parent_part_id, которая создает первые результаты хирархии. если мы посмотрим на эти результаты, то заметим, что нам нужно 2 части_и_2, каждая из которых нуждается в 10 части_и_3 - эта ветвь вносит 20 в общую сумму для части 3, позже аналогичным образом требуется еще 10 часть 3, в общей сложности 30 – Randy

ответ

5

В Oracle 11 R2 его можно с common table expression:

Тест данные:

-- drop table assembly; 

create table assembly (
    part_id    number, 
    parent_part_id  number, 
    quantity    number 
); 

insert into assembly values (2, 1, 2); 
insert into assembly values (3, 2, 10); 
insert into assembly values (4, 1, 2); 
insert into assembly values (5, 4, 1); 
insert into assembly values (3, 5, 5); 

ЗЕЬЕСТ:

select 
    part_id, 
    sum(quantity_used) as quantity 
from (
    with assembly_hier (lvl, part_id, quantity, quantity_used) as (
    select 
     1  lvl, 
     part_id, 
     quantity , 
     quantity  quantity_used 
    from 
     assembly 
    where 
     parent_part_id = 1 
    union all 
    select 
     assembly_hier.lvl  + 1 lvl, 
     assembly  .part_id, 
     assembly  .quantity, 
     assembly_hier.quantity_used * assembly.quantity quantity_used 
    from 
     assembly_hier, assembly 
    where 
     assembly_hier.part_id = assembly.parent_part_id 
) 
    select * from assembly_hier 
) 
group by part_id 
order by part_id; 

Редактировать До Ora11R2, он может работать с model clause:

select 
    part_id, 
    sum(quantity) quantity 
from (
    select 
    lvl 
    parent_part_id, 
    part_id, 
    quantity 
    from (
    select 
     lvl, 
     parent_part_id, 
     part_id, 
     quantity 
    from (
     select 
     rownum r, 
     level lvl, 
     parent_part_id, 
     part_id, 
     quantity 
     from 
     assembly 
     start with parent_part_id = 1 
     connect by parent_part_id = prior part_id 
    ) 
) 
    model 
    dimension by (lvl, part_id) 
    measures (quantity, parent_part_id) 
    rules upsert (
     quantity[  any, any   ] order by lvl, part_id = quantity[cv(lvl) , cv(part_id)] * 
              nvl(quantity[cv(lvl)-1, parent_part_id[cv(lvl), cv(part_id)] ], 1) 
    ) 
) 
group by part_id 
order by part_id; 

Edit II Другой возможностью было бы подвести логарифмы количества, а затем взять показатель на сумму в:

select 
    part_id, 
    sum(quantity) quantity 
from (
    select 
    part_id, 
    exp(sum(quantity_ln) over (partition by new_start order by r)) quantity 
    from (
    select 
     r, 
     lvl, 
     part_id, 
     quantity_ln, 
     sum(new_start) over(order by r) new_start 
    from (
     select 
     rownum r, 
     level lvl, 
     part_id, 
     ln(quantity) quantity_ln, 
     nvl(lag(connect_by_isleaf,1) over (order by rownum),0) new_start 
     from assembly 
     start with parent_part_id = 1 
     connect by parent_part_id = prior part_id 
    ) 
) 
) 
group by part_id 
order by part_id 
; 
+0

спасибо - у меня, к сожалению, проблема обратной совместимости с оракулом 10. Попытка понять, что происходит выше, похоже, что рекурсия встроена в определение assembly_hier, поскольку она ссылается сама по себе, но я не совсем уверен, что это работает - он взрывается в моем экземпляре 10g с неподдерживаемыми ошибками псевдонимов столбцов. – Randy

+0

Да, он не будет работать на Oracle ниже 11R2. –

+0

спасибо за помощь - я закончил с рабочим решением, размещенным здесь, но очень ценю ваши подробные ответы. – Randy

0

я оказался здесь: это работает на оракула 10 и 11, CONNECT_BY_ISLEAF могут быть использованы настроить логику, хотите ли вы суммировать только листья или все узлы.

select part_id, new_rec.quantity*sum(math_calc(math,2)) m, unit_of_measure 
from (SELECT rownum, level lvl, part_id, quantity, unit_of_measure 
      , connect_by_isleaf || sys_connect_by_path(quantity,'*') math 
from assembly 
start with parent_part_id = new_rec.part_id 
connect by parent_part_id = prior part_id) p 
group by part_id, unit_of_measure 
Смежные вопросы