2016-11-04 10 views
0

過去数日間、私の課題の解決に取り組んでおり、答えが見つからないようです。Oracle SQLのconnect_by_rootとサブクエリ

はじめに:という単一のテーブルがあるとします。各個人には、name,bossおよびpositionという3つのフィールドがあります。 nameがプライマリキーである場合は、の文字列との外部キーが異なる人のnameを指しています。 Person1 - > Person2 - > Person3 - > Person4のような通常のツリーを作成します。ここでPerson4は最高のボス、Person1はルートです。簡単にするためのは、誰もが3つの以上のボスを持っていない、とPerson4はヘッドボスであると仮定しよう

例パス:

Person7 - > Person4

Person6(nullに等しいbossを持つ唯一の人。) - > Person8 - > Person4

PERSON2 - > Person8 - > Person4

だから私の割り当ては言う:階層ORDに表示されるクエリを作成ER等しいposition「労働者」又は「マネージャ」のみツリー操作を使用して(CONNECT_BY_ROOT等による接続)とサブクエリ

出力テーブルを持つすべての人のために、各ボスの名前は5列で構成しなければなりません。

Name | Position | Boss 1 | Boss 2 | Boss 3 

任意のボス列がNULLになる場合、私はいくつかの空白を挿入する必要があります。

これは、これまでのところ、私のクエリです:

select 
    case  
    when l = 1 then name else ' ' end as "Name", 
    position, 
    case 
    when l = 2 then name else ' ' end as "Boss 1", 
    case 
    when l = 3 then name else ' ' end as "Boss 2", 
    case 
    when l = 4 then name else ' ' end as "Boss 3"  
from (
    select 
    connect_by_root position as position, 
    level as l, 
    name 
    from 
    People 
    connect by prior 
    boss = name 
    start with 
    position = 'Worker' 
    or position = 'Manager' 
); 

それはちょっとトリックを行いますが、ツリーの各レベルは、それは私が避けるために持っている何か、新しい行です。私はこのクエリがなぜこのような結果をもたらすのか知っていますが、各ステップで新しい行を作成せずにツリーをどのようにトラバースさせるかわかりません。

マイ結果:

Name |Position|Boss 1|Boss 2|Boss 3 

JOHN WORKER     
     WORKER HENRY    
     WORKER   PETER  
TERRY WORKER      
     WORKER PETER    
ALICE WORKER     
     WORKER PETER    
BILL MANAGER      
     MANAGER JAMES    
     MANAGER   PETER  

これは私が達成したい結果である:

Name |Position|Boss 1|Boss 2|Boss 3 

JOHN WORKER HENRY PETER   

TERRY WORKER PETER     

ALICE WORKER PETER     

BILL MANAGER JAMES PETER     

はそれを動作させるためにピボットのような洗練された機能を使用してせずにすべてのソリューションはありますか?

+0

ただ、 'max'または' min'機能。 '... else else '' end ...'を '... name else null end ... 'と置き換えてください。 – Dmitry

+0

これらの関数はサブクエリに変換されますか? – Karatte

答えて

0

それは、この(私がチェックするために何のソースデータを持っていない)のように動作するはずです:

select 
    root_name as "Name", 
    max(position), 
    max(case when l = 2 then name else null end) as "Boss 1", 
    max(case when l = 3 then name else null end) as "Boss 2", 
    max(case when l = 4 then name else null end) as "Boss 3"  
from (
    select 
    connect_by_root position as position, 
    connect_by_root name as root_name, 
    level as l, 
    name 
    from 
    People 
    connect by prior 
    boss = name 
    start with 
    position = 'Worker' 
    or position = 'Manager' 
) 
group by root_name; 
+0

あなたはgroup by節を紛失していますが、これはピボットの一形態です。私はOPが特にやりたいとは思っていません。 – Boneist

+1

@Boneistああ、ありがとう!元のクエリで最小限の修正を加えようとしました。 – Dmitry

+0

魅力的に働くようになりました。今は解決策について考える必要があります。 'max'関数はここで何をしますか? root_nameで 'group by 'することができる回避策ですか?それは "nullと名前から最大値を選ぶ"のように何かするので、常に名前を返しますか? – Karatte

0

それは純粋にCONNECT_BY_ROOTCONNECT_BY_ISLEAFSYS_CONNECT_BY_PATHを使用することにより、必要なピボットなどせずにこれを行うことは可能ですが、プラスREGEXP_SUBSTRの賢明な使用:

WITH people AS (SELECT 'JOHN' name, 'WORKER' position, 'HENRY' boss FROM dual UNION ALL 
       SELECT 'HENRY' name, 'CFO' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'TERRY' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'ALICE' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'JAMES' name, 'CIO' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'FRED' name, 'MANAGER' position, NULL boss FROM dual UNION ALL 
       SELECT 'BILL' name, 'MANAGER' position, 'JAMES' boss FROM dual UNION ALL 
       SELECT 'PETER' name, 'CEO' position, 'FRED' boss FROM dual) 
-- end of mimicking your people table with some sample data in it 
-- you wouldn't need the above, just use the query below: 
SELECT connect_by_root name AS name, 
     connect_by_root position AS position, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 1) boss1, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 2) boss2, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 3) boss3 
FROM people 
WHERE connect_by_isleaf = 1 
CONNECT BY PRIOR boss = name 
START WITH position IN ('WORKER', 'MANAGER'); 

NAME POSITION BOSS1 BOSS2 BOSS3 
----- -------- ----- ----- ----- 
ALICE WORKER PETER FRED 
BILL MANAGER JAMES PETER FRED 
FRED MANAGER    
JOHN WORKER HENRY PETER FRED 
TERRY WORKER PETER FRED 

CONNECT_BY_ISLEAF行が葉行(1)であるか否かを判断しますかnot(0)。したがって、CONNECT_BY_ROOTを使用してルート値を識別できるように、CONNECT_BY_ISLEAFを使用して、どの行が終了行であるかを知ることができます。

SYS_CONNECT_BY_PATHは、それまでのすべての値のパスを生成します。したがって、葉の行には、必要な値がすべて含まれます。この生成された文字列を解析して、パス区切り文字を含まない第1、第2などの部分を取得することができます。