◆環境
Oracle 12.1.0.2.0
◆質問内容
以下の階層構造のテーブルがあります。
ID | 名称 | 親ID | フラグ |
---|---|---|---|
1 | A | null | 0 |
2 | B | 1 | 0 |
3 | C | 2 | 1 |
4 | D | 3 | 0 |
5 | E | 1 | 1 |
6 | F | 5 | 0 |
7 | G | 1 | 0 |
自レコードよりも上位の階層で、かつフラグが1になっている階層の名称を取得することは可能でしょうか。
上位階層のレコードでフラグが1になるレコードは必ず1つしかありません。
ID | 名称 | 特定階層の名称 |
---|---|---|
1 | A | |
2 | B | |
3 | C | |
4 | D | C |
5 | E | |
6 | F | E |
7 | G |
また、階層レベルが2の階層の名称を取得することは可能でしょうか。
ID | 名称 | 階層レベル2の名称 |
---|---|---|
1 | A | |
2 | B | B |
3 | C | B |
4 | D | B |
5 | E | E |
6 | F | E |
7 | G | G |
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
ベストアンサー
「特定階層の名称」、「階層レベル2の名称」は階層の上限を決めてその階層数分の自己結合を行う必要があり、階層の上限を設定で行うような場合には、動的SQLが必要になります。
こういった階層構造には再帰を使用すると便利です。
津島博士のパフォーマンス講座(第31回 再帰的問合せについて)
他の言語の再帰とは違い、SQLの再帰は単なる順次読出しですので、そこまで難しくはありません。
質問のテーブルをtbl
とし再帰結合した結果をvtbl
とすると、再帰を使用した問い合わせは以下になります。
SQL
1with vtbl(階層レベル, 特定階層の名称, 階層レベル2の名称, id, 名称, 親ID, フラグ) as ( 2 select 1, Null, Null 3 , tbl.* 4 from tbl where 親ID is null 5 union all 6 select vtbl.階層レベル+1 7 , case when vtbl.フラグ=1 then vtbl.名称 else vtbl.特定階層の名称 end 8 , case when vtbl.階層レベル=1 then tbl.名称 else vtbl.階層レベル2の名称 end 9 , tbl.id, tbl.名称, tbl.親ID, tbl.フラグ 10 from vtbl inner join tbl 11 on vtbl.id=tbl.親ID 12) 13select * from vtbl 14order by id
投稿2020/06/12 01:41
編集2020/06/14 02:09総合スコア25327
0
単純な方法としては、以下のような階層レベル(経路情報)を管理するテーブルを作成する方法が考えられます。
SQL
1create table 階層レベル (ID int, レベル int, 上位ID int);
データが追加されたときは、親IDのデータ群をレベル+1として格納します。
SQL
1insert into 階層レベル select <自分のID>,レベル,上位ID from 階層レベル where ID=<親ID>; 2insert into 階層レベル select <自分のID>,max(レベル)+1,<自分のID> from 階層レベル where ID=<自分のID>;
質問の例の場合は以下のようなinsert文になります。
SQL
1insert into 階層レベル values(1,1,1); 2insert into 階層レベル select 2,レベル,上位ID from 階層レベル where ID=1; 3insert into 階層レベル select 2,max(レベル)+1,2 from 階層レベル where ID=2; 4insert into 階層レベル select 3,レベル,上位ID from 階層レベル where ID=2; 5insert into 階層レベル select 3,max(レベル)+1,3 from 階層レベル where ID=3; 6insert into 階層レベル select 4,レベル,上位ID from 階層レベル where ID=3; 7insert into 階層レベル select 4,max(レベル)+1,4 from 階層レベル where ID=4; 8insert into 階層レベル select 5,レベル,上位ID from 階層レベル where ID=1; 9insert into 階層レベル select 5,max(レベル)+1,5 from 階層レベル where ID=5; 10insert into 階層レベル select 6,レベル,上位ID from 階層レベル where ID=5; 11insert into 階層レベル select 6,max(レベル)+1,6 from 階層レベル where ID=6; 12insert into 階層レベル select 7,レベル,上位ID from 階層レベル where ID=1; 13insert into 階層レベル select 7,max(レベル)+1,7 from 階層レベル where ID=7;
これにより、以下のレコードになります。
SQL
1select * from 階層レベル; 2+------+--------+--------+ 3| ID | レベル | 上位ID | 4+------+--------+--------+ 5| 1 | 1 | 1 | 6| 2 | 1 | 1 | 7| 2 | 2 | 2 | 8| 3 | 1 | 1 | 9| 3 | 2 | 2 | 10| 3 | 3 | 3 | 11| 4 | 1 | 1 | 12| 4 | 2 | 2 | 13| 4 | 3 | 3 | 14| 4 | 4 | 4 | 15| 5 | 1 | 1 | 16| 5 | 2 | 5 | 17| 6 | 1 | 1 | 18| 6 | 2 | 5 | 19| 6 | 3 | 6 | 20| 7 | 1 | 1 | 21| 7 | 2 | 7 | 22+------+--------+--------+
フラグが1の名称の取得は以下のとおりです。
SQL
1select * from 階層レベル a inner join 階層構造のテーブル b on a.上位ID=b.ID where b.フラグ=1 and a.ID<>b.ID; 2+------+--------+--------+------+------+------+--------+ 3| ID | レベル | 上位ID | ID | 名称 | 親ID | フラグ | 4+------+--------+--------+------+------+------+--------+ 5| 4 | 3 | 3 | 3 | C | 2 | 1 | 6| 6 | 2 | 5 | 5 | E | 1 | 1 | 7+------+--------+--------+------+------+------+--------+
レベル2の名称の取得は以下のとおりです。
SQL
1select * from 階層レベル a inner join 階層構造のテーブル b on a.上位ID=b.ID where a.レベル=2; 2+------+--------+--------+------+------+------+--------+ 3| ID | レベル | 上位ID | ID | 名称 | 親ID | フラグ | 4+------+--------+--------+------+------+------+--------+ 5| 2 | 2 | 2 | 2 | B | 1 | 0 | 6| 3 | 2 | 2 | 2 | B | 1 | 0 | 7| 4 | 2 | 2 | 2 | B | 1 | 0 | 8| 5 | 2 | 5 | 5 | E | 1 | 1 | 9| 6 | 2 | 5 | 5 | E | 1 | 1 | 10| 7 | 2 | 7 | 7 | G | 1 | 0 | 11+------+--------+--------+------+------+------+--------+
なお、以下のデメリットがあります。
- 冗長なデータを持つ必要がある。
- 行数が階層の深さなどに依存して多くなる。
【補足】
2階層以上の場合の出力例について説明します。
仮に質問の階層構造のテーブルのBのフラグが1で、Cのフラグが0の場合には以下のようにC、D、Fの上位階層の情報が表示できます。
ID | 名称 | 親ID | フラグ |
---|---|---|---|
1 | A | null | 0 |
2 | B | 1 | 1 |
3 | C | 2 | 0 |
4 | D | 3 | 0 |
5 | E | 1 | 1 |
6 | F | 5 | 0 |
7 | G | 1 | 0 |
SQL
1select * from 階層レベル a inner join 階層構造のテーブル b on a.上位ID=b.ID inner join 階層構造のテーブル c on a.ID=c.ID where b.フラグ=1 and a.ID<>b.ID; 2<= a(階層レベルの情報) => <==== b(フラグが1の名称) ===> <=== c(自レコードの情報) ===> 3+------+--------+--------+------+------+------+--------+------+------+------+--------+ 4| ID | レベル | 上位ID | ID | 名称 | 親ID | フラグ | ID | 名称 | 親ID | フラグ | 5+------+--------+--------+------+------+------+--------+------+------+------+--------+ 6| 3 | 2 | 2 | 2 | B | 1 | 1 | 3 | C | 2 | 0 | 7| 4 | 2 | 2 | 2 | B | 1 | 1 | 4 | D | 3 | 0 | 8| 6 | 2 | 5 | 5 | E | 1 | 1 | 6 | F | 5 | 0 | 9+------+--------+--------+------+------+------+--------+------+------+------+--------+
※わかりやすいように、階層構造のテーブルの自レコードの情報をcとして追加しました。
投稿2020/06/14 01:36
編集2020/06/14 10:31総合スコア416
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/14 05:47
2020/06/14 09:44
2020/06/14 10:33
0
階層問い合わせをご検討ください。
図でイメージするOracle DatabaseのSQL全集 第6回 階層問い合わせ Oracle SQLの各機能をイメージ図を交えて解説
自レコードよりも上位の階層でかつフラグが1になっている階層の名称を取得
各レコードをルート(最上位)とし、
上位レコードのPARENT_IDとIDが一致する下位レコードを問い合わせ、
FLAGが1のレコードの名前を取得する
SQL
1SELECT 2 CONNECT_BY_ROOT(ID) AS ROOT_ID 3, CONNECT_BY_ROOT(NAME) AS ROOT_NAME 4, CASE 5 WHEN 6 FLAG = 1 7 AND CONNECT_BY_ROOT(ID) != ID --自レコードのフラグが1の場合は表示しない 8 THEN NAME 9 ELSE NULL 10 END AS FLAG1_NAME 11FROM 12 TBL 13WHERE 14 CONNECT_BY_ISLEAF = 1 -- LEAF行だけを取得 15CONNECT BY 16 PRIOR PARENT_ID = ID -- PARENT_IDで下位レコードのIDと紐づける 17AND PRIOR FLAG != 1 -- FLAGが1なら下位レコードを問い合わせない 18ORDER BY 19 CONNECT_BY_ROOT(ID)
階層レベルが2の階層の名称を取得
各レコードをルート(最上位)とし、
上位レコードのPARENT_IDとIDが一致する下位レコードを問い合わせ、
親レコードのPARENT_IDがNULLではないレコードまで問い合わせる
※親レコードのPARENT_IDがNULL = 階層レベル2
SQL
1SELECT 2 CONNECT_BY_ROOT(ID) AS ROOT_ID 3, CONNECT_BY_ROOT(NAME) AS ROOT_NAME 4, NVL2(PARENT_ID,NAME,NULL) LEVEL2_NAME 5FROM 6 TBL 7WHERE 8 CONNECT_BY_ISLEAF = 1 -- LEAF行だけを取得 9CONNECT BY 10 PRIOR PARENT_ID = ID -- PARENT_IDで下位レコードのIDと紐づける 11AND PARENT_ID IS NOT NULL -- 親レコードのPARENT_IDがNULLは対象にしない(=レベル2) 12ORDER BY 13 CONNECT_BY_ROOT(ID)
投稿2020/06/12 00:02
編集2020/06/12 01:54総合スコア103
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
複数の表からデータを取り出して表示させる 同じテーブルに対して、社員用のデータ取得用と上司のデータ取得用の2つを用意する、を参考にできるでしょう。
投稿2020/06/11 13:30
総合スコア16417
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/12 02:32
2020/06/14 09:59 編集
2020/06/15 09:00
2020/06/15 09:23