2016-07-23 4 views
1

私は組織単位のためにツリー状の構造(部署など)を持つテーブルを持つOracle 12cのデータベースがあります。だから、SQLクエリ

CREATE TABLE "OUS" (
    "ID" NUMBER(38,0) NOT NULL ENABLE, 
    "NAME" VARCHAR2(255 CHAR) NOT NULL ENABLE, 
    "PARENT_ID" NUMBER(38,0), 
    PRIMARY KEY("ID"), 
    CONSTRAINT "OUS_HIERARCHY_FK" FOREIGN KEY ("PARENT_ID") REFERENCES "OUS" ("ID") ON DELETE CASCADE 
); 

を、

| id | name   | parent_id | 
| -: | ------------- | --------: | 
| 1 | Root   | (NULL) | 
| 2 | Territorial 1 |   1 | 
| 3 | Regional 1-1 |   2 | 
| 4 | Alpha dept |   3 | 
| 5 | Beta dept  |   3 | 
| 6 | Regional 1-2 |   2 | 
| 7 | Gamma dept |   6 | 
| 8 | Delta dept |   7 | 
| 9 | Territorial 2 |   1 | 
| 10 | Regional 2-1 |   9 | 
| 11 | Epsilon dept |  10 | 
| 12 | Zeta dept  |  10 | 

のような構造を持つあなたが好きSQLでそれを作成することができます。

INSERT INTO ous (id, name, parent_id) VALUES (13, 'Root',   NULL); 
INSERT INTO ous (id, name, parent_id) VALUES ( 2, 'Territorial 1', 13); 
INSERT INTO ous (id, name, parent_id) VALUES ( 1, 'Regional 1-1',  2); 
INSERT INTO ous (id, name, parent_id) VALUES ( 5, 'Alpha dept',  1); 
INSERT INTO ous (id, name, parent_id) VALUES ( 4, 'Beta dept',  1); 
INSERT INTO ous (id, name, parent_id) VALUES ( 6, 'Regional 1-2',  2); 
INSERT INTO ous (id, name, parent_id) VALUES ( 7, 'Gamma dept',  6); 
INSERT INTO ous (id, name, parent_id) VALUES ( 8, 'Delta dept',  6); 
INSERT INTO ous (id, name, parent_id) VALUES ( 9, 'Territorial 2', 13); 
INSERT INTO ous (id, name, parent_id) VALUES ( 3, 'Regional 2-1',  9); 
INSERT INTO ous (id, name, parent_id) VALUES (15, 'Epsilon dept',  3); 
INSERT INTO ous (id, name, parent_id) VALUES (12, 'Zeta dept',  3); 

特定の条件(たとえばname = 'Alpha' OR name = 'Epsilon)に一致するOUの一部を見つけ、これらのOUとその祖先のサブツリーを取得する必要があります。例えば

| id | name   | parent_id | 
| -: | ------------- | --------: | 
| 1 | Root   | (NULL) | ← Ancestor of Alpha and Epsilon 
| 2 | Territorial 1 |   1 | ← Ancestor of Alpha 
| 3 | Regional 1-1 |   2 | ← Ancestor of Alpha 
| 4 | Alpha dept |   3 | ← Matches the WHERE clause! 
| 9 | Territorial 2 |   1 | ← Ancestor of Epsilon 
| 10 | Regional 2-1 |   9 | ← Ancestor of Epsilon 
| 11 | Epsilon dept |  10 | ← Matches the WHERE clause! 

私は、様々なHierarchical and recursive queries in SQL見てる:Oracle Hierarchical queriesCTEsが、私にそのような結果を返すことができますクエリを把握することはできません。

私はOracle Database 12cを使用しています。

私のようなクエリを試してみた:

はまた、私が試した(その祖先はフィルタリングされている)

SELECT ous.* FROM ous 
WHERE name = 'Alpha' OR name = 'Epsilon' 
START WITH 
    parent_id IS NULL 
CONNECT BY 
    PRIOR id = parent_id 
ORDER SIBLINGS BY name; 

をしかし、すべての行に適用されるWHEREとして、それが0行を返す:

WITH RECURSIVE all_nodes (id, parent_id, name) AS (
    SELECT ous.id, ous.parent_id, name FROM ous WHERE (name = 'Alpha' OR name = 'Epsilon') 
    UNION 
    SELECT ous.id, ous.parent_id, name FROM ous INNER JOIN all_nodes ON ous.parent_id = all_nodes.id 
) 
SELECT * FROM all_nodes INNER JOIN ous ON all_nodes.id = ous.id ORDER BY name; 

しかし、それはあなたが再帰的にこれを行うことができ、エラーSQL Error [905] [42000]: ORA-00905: keyword is missing

答えて

3

を返します。 CTE:

with t(name, id, parent_id) as (
     select name, id, parent_id 
     from ous 
     where name in ('alpha', 'epsilon') 
     union all 
     select ous.name, ous.id, ous.parent_id 
     from t join 
      ous 
      on ous.id = t.parent_id 
    ) 
select distinct t.id, t.name, t.parent 
from t 
order by t.id; 

select distinctはおそらく必要ありません。

再帰的CTEは標準SQLであるという利点があるため、ロジックはさまざまなデータベースでサポートされています。

+0

迅速な返信ありがとうございます。残念ながら、クエリは祖先のない一致する行のみを返します。また、再帰CTEにはエイリアス・リスト(ORA-32039)が必要であるため、最初の行を '(name、id、parent_id)を'( ')に変更しました。 – Envek

+0

それを理解してください: 'ON'節の平等は' on ous.id = t.parent_id'と逆にする必要があります。今それは動作します! – Envek

+0

Oracle構文の 'ORDER SIBLINGS BY'のように、親から子へと育っていないidsの場合、階層と名前で結果を並べ替えることはできますか? – Envek

2

もちろん、これに対して階層クエリを使用できます。問題は、複数の葉で開始すると、ある時点で重複する行が始まることになります。重複を「別個」で削除することはできますが、特に非常に大きなテーブルや、あまりにも多くの葉で開始すると、パフォーマンスが低下します。最終的には、再帰的なクエリは記述が難しい場合がありますが、階層的なクエリよりも効率的です。

完全性のために、ここでは階層的解決法があります。 SCOTTスキーマでのEMP表の使用最初にストレート階層クエリ(および出力に重複)を表示し、次に「別個」のバージョンを表示します。

select empno, mgr 
from scott.emp 
start with empno in (7902, 7788) 
connect by prior mgr = empno 
; 

    EMPNO  MGR 
---------- ---------- 
     7788  7566 
     7566  7839 
     7839 
     7902  7566 
     7566  7839 
     7839 


select distinct empno, mgr 
from scott.emp 
start with empno in (7902, 7788) 
connect by prior mgr = empno 
; 

    EMPNO  MGR 
---------- ---------- 
     7839 
     7566  7839 
     7902  7566 
     7788  7566 
+0

ありがとう!私は成功しました 'SELECT DISTINCT ous。* FROM ous名前に 'Alpha%' OR name LIKE 'Epsilon%'先行するparent_id = id ORDER SIBLINGS BY name;で始まりましたが、レコードの順序は奇妙です。それで何かできますか?私は質問に 'CREATE TABLE'と' INSERT'を追加しました。 – Envek

+0

希望の注文は何ですか? SELECT句でキーワードDISTINCTを使用すると、階層問合せのORDER BY句に入る内容に制限があることに注意してください。たとえば、ルートを上にしたい場合はLEVEL DESCをORDER BYすることができますが、そうするにはSELECT節に疑似列LEVELを含める必要があります。 – mathguy