2016-07-23 4 views
1

에서 그들의 조상과 일치하는 행을 반환하는 나는 (등 부서와) 나무와 같은 조직 단위 구조의 테이블 오라클 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; 

을하지만 모든 행에 적용되는 경우 (그래서 조상이 필터링되는) 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'와 같이 부모에서 자식으로 성장하지 않는 ID가있는 경우 계층 구조 및 이름별로 결과를 정렬 할 수 있습니까? – Envek

2

물론 계층 적 쿼리를 사용할 수 있습니다. 문제는 여러 잎으로 시작한 경우 어느 시점에서 중복 행이 생기기 시작한다는 것입니다. "distinct"를 사용하여 중복을 제거 할 수 있지만 성능이 저하됩니다. 특히 매우 큰 테이블이나 너무 많은 나뭇잎으로 시작할 경우 성능이 저하됩니다. 하루가 끝날 때 재귀 쿼리는 작성하기가 더 어렵지만 계층 적 쿼리보다 효율적입니다.

완전성을 위해 다음은 계층 적 솔루션입니다. 설명을 위해 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. *에서 'Alpha %'또는 'LIKE'Epsilon % '와 같은 이름으로 시작합니다. parent_id = id ORDER SIBLINGS BY name;'하지만 레코드 순서가 이상합니다. 그걸로 뭔가를 할 수 있습니까? 내가 질문에'CREATE TABLE'과'INSERT'을 추가했습니다. – Envek

+0

원하는 순서는 무엇입니까? SELECT 절에 키워드 DISTINCT를 사용하면 계층 적 쿼리의 ORDER BY 절에서 수행 할 수있는 것에 대한 제한 사항이 있습니다. 예를 들어, 최상위에 루트를 넣고 싶다면 LEVEL DESC를 ORDER 할 수 있지만 SELECT 절에 의사 열 LEVEL을 포함시켜야합니다. – mathguy