質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

4回答

3304閲覧

Oracle 階層構造テーブルで特定条件のレコードのカラムを取得したい

nobinobiko

総合スコア11

Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

1クリップ

投稿2020/06/11 13:13

◆環境
Oracle 12.1.0.2.0

◆質問内容
以下の階層構造のテーブルがあります。

ID名称親IDフラグ
1Anull0
2B10
3C21
4D30
5E11
6F50
7G10

自レコードよりも上位の階層で、かつフラグが1になっている階層の名称を取得することは可能でしょうか。
上位階層のレコードでフラグが1になるレコードは必ず1つしかありません。

ID名称特定階層の名称
1A
2B
3C
4DC
5E
6FE
7G

また、階層レベルが2の階層の名称を取得することは可能でしょうか。

ID名称階層レベル2の名称
1A
2BB
3CB
4DB
5EE
6FE
7GG

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
sazi

総合スコア25206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toyotaku

2020/06/12 02:32

特定階層の名称について、 「自レコード"よりも"上位の階層」とあります。 case when vtbl.フラグ=1 then vtbl.名称 end では、「自レコードの1つ上位の階層」になるため case when vtbl.フラグ=1 then vtbl.名称 else vtbl.SNAME end とすべきかと思います。
sazi

2020/06/14 09:59 編集

@toyotakuさん フォローありがとうございます。 ほんとですね。なぜか一つ上と思い込んでました。修正します。
nobinobiko

2020/06/15 09:00

想定するデータが取得できました。 ありがとうございました。
Orlofsky

2020/06/15 09:23

https://www.oracle.com/jp/technical-resources/article/tsushima/tsushima-31.html 動画を見直そうと思ったけど、 >津島博士のパフォーマンス講座「パフォーマンス問題はなぜ起きるのか」【WMV】 【MP4】 【PDF】 動画【WMV】 【MP4】が2つともリンク切れ 【PDF】は読めたけど、その中の https://www.oracle.com/technetwork/jp/ondemand/db-new/b-13-screen-1448415-ja.pdf の連絡先 https://blogs.oracle.com/oracle4engineer/entry/otn_ondemand_questionnaire がリンク切れ https://www.oracle.com/jp/technical-resources/article/tsushima/tsushima-31.html に戻って、いちばん下の >津島博士の記事についてのご質問はこちらまでお願いいたします。 https://secure.oracle.co.jp/otn/inquiry/index.php もリンク切れ 昔日の面影はなくなったようです。
guest

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フラグ
1Anull0
2B11
3C20
4D30
5E11
6F50
7G10

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
etsuhisa

総合スコア416

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2020/06/14 02:24 編集

階層レベルを設定したとして、それをどう利用するのでしょう?
etsuhisa

2020/06/14 05:47

SQLの具体例を記載しました。insert文もレベルが逆のほうが利用しやすいので修正しました。
sazi

2020/06/14 09:44

> 自レコードよりも上位の階層で、かつフラグが1になっている階層の名称 について、一つ上の階層しか取得出来ていないですよ。 階層構造を制御するには、経路情報を持たなければ出来ないと思います。
etsuhisa

2020/06/14 10:33

階層レベルのテーブルが経路情報であることを記載しました。また、2階層以上の場合の出力例についての説明を補足として追加しました。
guest

0

階層問い合わせをご検討ください。

SQLの抽出結果を階層構造で表示するテクニック

図でイメージする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
toyotaku

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

複数の表からデータを取り出して表示させる 同じテーブルに対して、社員用のデータ取得用と上司のデータ取得用の2つを用意する、を参考にできるでしょう。

投稿2020/06/11 13:30

Orlofsky

総合スコア16415

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問