Oracle/Tuning2018. 12. 31. 11:08

UNION ALL 연산자의 Top-N 동작에 대한 내용을 살펴보자.


테스트를 위해 아래와 같이 데이터를 생성하자.

-- 1
DROP TABLE t1 PURGE;
DROP TABLE t2 PURGE;

CREATE TABLE t1 AS SELECT ROWNUM AS c1, LPAD ('X', 4000, 'X') AS c2 FROM XMLTABLE ('1 to 10');
CREATE TABLE t2 AS SELECT * FROM t1;
CREATE INDEX t1_x1 ON t1 (c1);
CREATE INDEX t2_x1 ON t2 (c1);

아래 쿼리는 UNION ALL 연산 결과에 Top-N을 사용했다. 자주는 아니지만 수평분할된 테이블이나 유사한 성격의 테이블에 대한 Top-N 처리가 필요할 때 종종 사용되는 패턴이다. t2 테이블은 블록 I/O가 발생하지 않았으므로 Top-N이 동작하는 것을 확인할 수 있다.

-- 2
SELECT c1, LTRIM (c2, 'X') AS c2
  FROM (SELECT * FROM (SELECT /*+ INDEX(T1) */ * FROM t1 WHERE c1 >= 1 ORDER BY c1)
        UNION ALL
        SELECT * FROM (SELECT /*+ INDEX(T2) */ * FROM t2 WHERE c1 >= 1 ORDER BY c1))
 WHERE ROWNUM <= 10;

-----------------------------------------------------------------------------
| Id  | Operation                       | Name  | Starts | A-Rows | Buffers |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |       |      1 |     10 |      12 |
|*  1 |  COUNT STOPKEY                  |       |      1 |     10 |      12 |
|   2 |   VIEW                          |       |      1 |     10 |      12 |
|   3 |    UNION-ALL                    |       |      1 |     10 |      12 |
|   4 |     VIEW                        |       |      1 |     10 |      12 |
|   5 |      TABLE ACCESS BY INDEX ROWID| T1    |      1 |     10 |      12 |
|*  6 |       INDEX RANGE SCAN          | T1_X1 |      1 |     10 |       2 |
|   7 |     VIEW                        |       |      0 |      0 |       0 | -- !
|   8 |      TABLE ACCESS BY INDEX ROWID| T2    |      0 |      0 |       0 |
|*  9 |       INDEX RANGE SCAN          | T2_X1 |      0 |      0 |       0 |
-----------------------------------------------------------------------------

N을 15로 늘리면 t1 테이블에서 5건만 조회되는 것을 확인할 수 있다.

-- 3
SELECT c1, LTRIM (c2, 'X') AS c2
  FROM (SELECT * FROM (SELECT /*+ INDEX(T1) */ * FROM t1 WHERE c1 >= 1 ORDER BY c1)
        UNION ALL
        SELECT * FROM (SELECT /*+ INDEX(T2) */ * FROM t2 WHERE c1 >= 1 ORDER BY c1))
 WHERE ROWNUM <= 15;

-----------------------------------------------------------------------------
| Id  | Operation                       | Name  | Starts | A-Rows | Buffers |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |       |      1 |     15 |      18 |
|*  1 |  COUNT STOPKEY                  |       |      1 |     15 |      18 |
|   2 |   VIEW                          |       |      1 |     15 |      18 |
|   3 |    UNION-ALL                    |       |      1 |     15 |      18 |
|   4 |     VIEW                        |       |      1 |     10 |      12 |
|   5 |      TABLE ACCESS BY INDEX ROWID| T1    |      1 |     10 |      12 |
|*  6 |       INDEX RANGE SCAN          | T1_X1 |      1 |     10 |       2 |
|   7 |     VIEW                        |       |      1 |      5 |       6 | -- !
|   8 |      TABLE ACCESS BY INDEX ROWID| T2    |      1 |      5 |       6 |
|*  9 |       INDEX RANGE SCAN          | T2_X1 |      1 |      5 |       1 |
-----------------------------------------------------------------------------

아래와 같은 방식으로도 Top-N을 사용할 수 있다. t1에서 5행을 조회하고, t2에서 나머지 행을 조회했다.

-- 4
SELECT c1, LTRIM (c2, 'X') AS c2
  FROM (SELECT * FROM (SELECT /*+ INDEX(T1) */ * FROM t1 WHERE c1 >= 1 ORDER BY c1) WHERE ROWNUM <= 5
        UNION ALL
        SELECT * FROM (SELECT /*+ INDEX(T2) */ * FROM t2 WHERE c1 >= 1 ORDER BY c1))
 WHERE ROWNUM <= 10;

------------------------------------------------------------------------------
| Id  | Operation                        | Name  | Starts | A-Rows | Buffers |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |       |      1 |     10 |      13 |
|*  1 |  COUNT STOPKEY                   |       |      1 |     10 |      13 |
|   2 |   VIEW                           |       |      1 |     10 |      13 |
|   3 |    UNION-ALL                     |       |      1 |     10 |      13 |
|*  4 |     COUNT STOPKEY                |       |      1 |      5 |       7 |
|   5 |      VIEW                        |       |      1 |      5 |       7 | -- !
|   6 |       TABLE ACCESS BY INDEX ROWID| T1    |      1 |      5 |       7 |
|*  7 |        INDEX RANGE SCAN          | T1_X1 |      1 |      5 |       2 |
|   8 |     VIEW                         |       |      1 |      5 |       6 |
|   9 |      TABLE ACCESS BY INDEX ROWID | T2    |      1 |      5 |       6 |
|* 10 |       INDEX RANGE SCAN           | T2_X1 |      1 |      5 |       1 |
------------------------------------------------------------------------------

12.1 버전부터 동작하는 PQ_CONCURRENT_UNION은 UNION ALL 연산자의 SET을 각각 병렬로 수행하기 때문에 UNION ALL 레벨의 Top-N이 동작하지 않는다.

-- 5
SELECT /*+ PQ_CONCURRENT_UNION */
       C1, LTRIM (C2, 'X') AS C2
  FROM (SELECT * FROM (SELECT /*+ INDEX(T1) */ * FROM T1 WHERE C1 >= 1 ORDER BY C1) WHERE ROWNUM <= 5
        UNION ALL
        SELECT * FROM (SELECT /*+ INDEX(T2) */ * FROM T2 WHERE C1 >= 1 ORDER BY C1))
 WHERE ROWNUM <= 10;

------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name     | Starts |    TQ  |IN-OUT| PQ Distrib | A-Rows | Buffers |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |          |      1 |        |      |            |     10 |       0 |
|*  1 |  COUNT STOPKEY                       |          |      1 |        |      |            |     10 |       0 |
|   2 |   PX COORDINATOR                     |          |      1 |        |      |            |     10 |       0 |
|   3 |    PX SEND QC (RANDOM)               | :TQ10000 |      0 |  Q1,00 | P->S | QC (RAND)  |      0 |       0 |
|*  4 |     COUNT STOPKEY                    |          |      2 |  Q1,00 | PCWC |            |     15 |      17 | -- !
|   5 |      VIEW                            |          |      2 |  Q1,00 | PCWP |            |     15 |      17 |
|   6 |       UNION-ALL                      |          |      2 |  Q1,00 | PCWP |            |     15 |      17 |
|   7 |        PX SELECTOR                   |          |      2 |  Q1,00 | PCWP |            |      5 |       6 |
|*  8 |         COUNT STOPKEY                |          |      1 |  Q1,00 | PCWP |            |      5 |       6 | -- !
|   9 |          VIEW                        |          |      1 |  Q1,00 | PCWP |            |      5 |       6 |
|  10 |           TABLE ACCESS BY INDEX ROWID| T1       |      1 |  Q1,00 | PCWP |            |      5 |       6 |
|* 11 |            INDEX RANGE SCAN          | T1_X1    |      1 |  Q1,00 | PCWP |            |      5 |       1 |
|  12 |        PX SELECTOR                   |          |      2 |  Q1,00 | PCWP |            |     10 |      11 |
|  13 |         VIEW                         |          |      1 |  Q1,00 | PCWP |            |     10 |      11 |
|  14 |          TABLE ACCESS BY INDEX ROWID | T2       |      1 |  Q1,00 | PCWP |            |     10 |      11 |
|* 15 |           INDEX RANGE SCAN           | T2_X1    |      1 |  Q1,00 | PCWP |            |     10 |       1 |
------------------------------------------------------------------------------------------------------------------


'Oracle > Tuning' 카테고리의 다른 글

조인이 포함된 Top-N 쿼리  (0) 2019.01.07
고유 값 구하기  (0) 2019.01.04
조건 기술에 따른 MERGE 문의 성능 차이  (0) 2018.12.26
INLIST ITERATOR 성능 개선  (0) 2018.12.26
PX SEND 1 SLAVE 오퍼레이션  (0) 2018.12.21
Posted by 정희락_