본문 바로가기

Oracle/UNION, UNION ALL

Oracle UNION, UNION ALL example

오라클 WITH, UNION ALL 문장을 사용한 계층구조 커리 사용 예


UNION

2개의 테이블이나 결과집합을 합칠 때 동일한 행이 중복되지 않도록 합친다

UNION ALL

2개의 테이블이나 결과집합을 합칠 때 중복여부와 상관 없이 무조건 합친다

WITH 절 안에서 UNION ALL을 사용하면 반복적으로 호출되는 Recursive Member 커리가 되며 계층구조 출력기능을 수행할 수 있다


Hierarchical Queries Using Recursive WITH Clauses

https://oracle-base.com/articles/11g/recursive-subquery-factoring-11gr2


반복호출 WITH 절 (Recursive WITH Clauses)

WITH 절 안에 2개 이상의 커리가 UNION ALL으로 연결되어 있는 커리를 반복호출 WITH 절(Recursive WITH Clauses)이라고 한다

UNION ALL 위에 있는 커리를 Anchor Member (고정멤버 커리)라고 하고, UNION ALL 이후에 있는 커리를 Recursive Member (반복멤버 커리)라고 한다. Anchor Member 커리는 트리구조상의 최상위 노드(루트노드)를 가져오는 역할을 하며, Recursive Member 커리는 루트노드에 하위노드를 반복적으로 추가하는 역할을 한다


기본적인 계층구조(Hierarchical Query) 커리

-- WITH절의 컬럼수와 SELECT 컬럼수가 일치해야 한다
WITH t1(empno,ename,mgr)AS
(
  SELECT empno, ename, mgr FROM emp WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr FROM emp e JOIN t1 ON e.mgr = t1.empno
)
SELECT * FROM t1; -- SELECT 절이 반드시 요구됨


위의 커리 실행결과 ( 깊이우선(Depth First)이 아닌 너비우선(Breadth First)으로 출력됨 )

7839	KING	
7566	JONES	7839
7698	BLAKE	7839
7782	CLARK	7839
7499	ALLEN	7698
7521	WARD	7698
7654	MARTIN	7698
7844	TURNER	7698
7900	JAMES	7698
7902	FORD	7566
7934	MILLER	7782
7369	SMITH	7902


정렬조건 지정

깊이 우선, 너비 우선, 동일차수일 경우 정렬기준 컬럼 지정

  • BREADTH FIRST BY : Sibling rows are returned before child rows are processed.
  • DEPTH FIRST BY : Child rows are returned before siblings are processed
WITH t1(empno,ename,mgr)AS
(
  SELECT empno, ename, mgr FROM emp WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr FROM emp e JOIN t1 ON e.mgr = t1.empno
)
SEARCH DEPTH FIRST BY empno SET ordering
SELECT * FROM t1 ORDER BY ordering;

위의 쿼리에서 SEARCH DEPTH FIRST BY empno  : 트리 구조에서 동일차수(한 노드 안에 있는 형제노드)의 노드들 사이에는 사번을 이용하여 정렬한다.

위의 쿼리에서 SET ordering : SET 다음에 오는 단어는 테이블로부터 데이터를 가져오는 순서가 정수로 저장되는 임시 컬럼이므로 주 쿼리에서 SELECT ordering, empno FROM t1 과 같은 방법으로 사용할 수 있다


위의 문장 실행결과

7839	KING		1
7566	JONES	7839	2
7902	FORD	7566	3
7369	SMITH	7902	4
7698	BLAKE	7839	5
7499	ALLEN	7698	6
7521	WARD	7698	7
7654	MARTIN	7698	8
7844	TURNER	7698	9
7900	JAMES	7698	10
7782	CLARK	7839	11
7934	MILLER	7782	12


WITH 반복커리에서 트리의 차수(LEVEL) 얻기

WITH t1(empno,ename,mgr,lvl)AS
(
  SELECT empno, ename, mgr, 1 AS lvl FROM emp WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr, t1.lvl+1 AS lvl FROM emp e JOIN t1 ON e.mgr = t1.empno
)
SEARCH DEPTH FIRST BY empno SET ordering
SELECT * FROM t1 ORDER BY ordering;


위의 문장 실행결과

7839	KING		1	1
7566	JONES	7839	2	2
7902	FORD	7566	3	3
7369	SMITH	7902	4	4
7698	BLAKE	7839	2	5
7499	ALLEN	7698	3	6
7521	WARD	7698	3	7
7654	MARTIN	7698	3	8
7844	TURNER	7698	3	9
7900	JAMES	7698	3	10
7782	CLARK	7839	2	11
7934	MILLER	7782	3	12


계층구조 커리 결과 트리의 차수를 이용하여 시각적인 계층구조를 표시하는 예

WITH t1(empno,ename,mgr,lvl)AS
(
  SELECT empno, ename, mgr, 0 AS lvl FROM emp WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr, t1.lvl+1 AS lvl FROM emp e JOIN t1 ON e.mgr = t1.empno
)
SEARCH DEPTH FIRST BY empno SET ordering
SELECT empno, LPAD(' ', lvl*3, ' ')||ename AS ename, mgr FROM t1 ORDER BY ordering;


위의 문장 실행결과

7839	KING	
7566	  JONES	7839
7902	   FORD	7566
7369	     SMITH	7902
7698	  BLAKE	7839
7499	   ALLEN	7698
7521	   WARD	7698
7654	   MARTIN	7698
7844	   TURNER	7698
7900	   JAMES	7698
7782	  CLARK	7839
7934	   MILLER	7782


WITH 절에 테이블 내의 총 레코드 수를 포함하는 예

WITH t1(empno,ename,mgr,lvl, totalRows)AS ( SELECT empno, ename, mgr, 0 AS lvl, (SELECT COUNT(*) FROM emp) totalRows FROM emp WHERE mgr IS NULL UNION ALL SELECT e.empno, e.ename, e.mgr, t1.lvl+1 AS lvl, t1.totalRows FROM emp e JOIN t1 ON e.mgr = t1.empno ) SEARCH DEPTH FIRST BY empno SET ordering SELECT empno, LPAD(' ', lvl*3, ' ')||ename AS ename, mgr, totalRows FROM t1 ORDER BY ordering;


위의 기능에 추가하여 레코드 중에서 한 화면에 보여줄 특정 행(Row)만을 가져오는 예

SEARCH DEPTH FIRST BY empno DESC SET ordering : 깊이우선 탐색으로 가져온 결과를 empno DESC 순으로 정렬하여 출력

위의 DESC 대신 ASC, 혹은 생략하면 오름차순이 된다

WITH t1(empno,ename,mgr,lvl, totalRows)AS
(
  SELECT empno, ename, mgr, 0 AS lvl, (SELECT COUNT(*) FROM emp) totalRows 
  FROM emp 
  WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr, t1.lvl+1 AS lvl, t1.totalRows 
  FROM emp e 
  JOIN t1 
  ON e.mgr = t1.empno
)
SEARCH DEPTH FIRST BY empno DESC SET ordering
SELECT empno, LPAD(' ', lvl*3, ' ')||ename AS ename, mgr, totalRows 
FROM t1 
WHERE ordering BETWEEN 1 AND 5
ORDER BY ordering;


page 라는 임시컬럼을 생성하여 특정 페이지에 속하는 행들을 가져오는 예 (한 화면에 5행씩 보여주는 경우)

WITH t1(empno,ename,mgr,lvl, totalRows)AS
(
  SELECT empno, ename, mgr, 0 AS lvl, (SELECT COUNT(*) FROM emp) totalRows 
  FROM emp 
  WHERE mgr IS NULL
  UNION ALL
  SELECT e.empno, e.ename, e.mgr, t1.lvl+1 AS lvl, t1.totalRows 
  FROM emp e 
  JOIN t1 
  ON e.mgr = t1.empno
)
SEARCH DEPTH FIRST BY empno SET ordering
SELECT * FROM
(
  SELECT empno, LPAD(' ', lvl*3, ' ')||ename AS ename, mgr, totalRows, TRUNC((ordering-1)/5)+1 AS page 
  FROM t1 
  ORDER BY ordering
)
WHERE page=1;