2011-01-24 5 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 

または何か類似した適切な...おそらく、これを解析して結果を返す関数を作ることで、問題が解決されます。誰もこれが大変だと思いますか?

Oracleの11 R2では
+0

関連ノートで、私は申し訳ないが、私は質問を理解していない*私は、トップレベルのアセンブリ(パート1)を作るために必要な各部品の合計数を計算する方法を教えてください*と期待される結果をどう導き出すか。 –

+0

@Rene - パート1は、最初の階層化結果を作成する元のparent_part_idです。これらの結果を見ると、part_id 2が2つ必要であることに気付きます。それぞれpart_id 10が必要です。3 - このブランチは、パート3の合計に20を払います。後で同様の方法で10個のパート3が必要です。合計で30 – Randy

答えて

5

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文:

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; 

編集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

ありがとう - 私は間違いなく、Oracle 10との下位互換性の問題があります。上記で何が起こっているのか理解しようとすると、assembly_hierの定義に組み込まれている再帰があるようです。しかし、私はこれがどういうふうに機能しているのかはまだ分かっていません。サポートされていない列エイリアシングエラーで10gのインスタンスに爆発します。 – Randy

+0

はい、11R2未満のOracleでは動作しません。 –

+0

助けてくれてありがとう - 私はここに掲載されている作業用のソリューションで終わったが、あなたの詳細な応答に非常に感謝する。 – Randy

0

私はここで終わった:これは、Oracle 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 
関連する問題