Oracle/Tuning2019. 10. 5. 15:44

12.2 버전에서 아우터 조인으로 행 복제를 수행하면 쿼리의 성능이 저하될 수 있다. 관련 내용을 살펴보자.


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

-- 1
DROP TABLE t1 PURGE;
CREATE TABLE t1 AS SELECT ROWNUM AS c1 FROM XMLTABLE ('1 to 1000000');

아래가 문제의 쿼리다. 11.2 버전에서는 소트 머지 조인으로 인해 계층 전개를 1번만 수행한 것을 확인할 수 있다.(Starts = 1)

-- 2 : 11.2
SELECT COUNT (*)
  FROM t1
  LEFT OUTER
  JOIN (SELECT LEVEL AS lv FROM DUAL CONNECT BY LEVEL <= 10)
    ON 1 = 1;

---------------------------------------------------------------------------------
| Id  | Operation                         | Name | Starts | A-Rows |   A-Time   |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |      |      1 |      1 |00:00:03.13 |
|   1 |  SORT AGGREGATE                   |      |      1 |      1 |00:00:03.13 |
|   2 |   MERGE JOIN OUTER                |      |      1 |     10M|00:00:02.85 |
|   3 |    TABLE ACCESS FULL              | T1   |      1 |   1000K|00:00:00.37 |
|   4 |    BUFFER SORT                    |      |   1000K|     10M|00:00:01.29 |
|   5 |     VIEW                          |      |      1 |     10 |00:00:00.01 | -- !
|   6 |      VIEW                         |      |      1 |     10 |00:00:00.01 |
|   7 |       CONNECT BY WITHOUT FILTERING|      |      1 |     10 |00:00:00.01 |
|   8 |        FAST DUAL                  |      |      1 |      1 |00:00:00.01 |
---------------------------------------------------------------------------------

아래는 12.2 버전의 실행계획이다. 12.2 버전도 소트 머지 조인으로 조인했지만 Lateral 뷰로 인해 계층 전개가 100만번 수행된 것을 확인할 수 있다.(START = 1000K) 이로 인해 과도한 수행 시간이 소요되었다.

-- 3 : 12.2
SELECT COUNT (*)
  FROM t1
  LEFT OUTER
  JOIN (SELECT LEVEL AS lv FROM DUAL CONNECT BY LEVEL <= 10)
    ON 1 = 1;

--------------------------------------------------------------------------------------------
| Id  | Operation                         | Name            | Starts | A-Rows |   A-Time   |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                 |      1 |      1 |00:00:14.29 |
|   1 |  SORT AGGREGATE                   |                 |      1 |      1 |00:00:14.29 |
|   2 |   MERGE JOIN OUTER                |                 |      1 |     10M|00:00:13.84 |
|   3 |    TABLE ACCESS FULL              | T1              |      1 |   1000K|00:00:00.45 |
|   4 |    BUFFER SORT                    |                 |   1000K|     10M|00:00:12.03 |
|   5 |     VIEW                          | VW_LAT_5B1BC3A4 |   1000K|     10M|00:00:08.29 | -- !
|   6 |      VIEW                         |                 |   1000K|     10M|00:00:06.70 |
|   7 |       CONNECT BY WITHOUT FILTERING|                 |   1000K|     10M|00:00:05.34 |
|   8 |        FAST DUAL                  |                 |   1000K|   1000K|00:00:00.19 |
--------------------------------------------------------------------------------------------

3번 쿼리는 12.2 버전에서 아래와 같이 변환된다. 바람직하지 못한 쿼리 변환으로 판단된다.

-- 4
SELECT COUNT(*)
  FROM t1
     , LATERAL (SELECT 0 FROM (SELECT LEVEL AS lv FROM DUAL CONNECT BY LEVEL<=10))(+) VW_LAT_5B1BC3A4;

해법은 단순하다. INNER JOIN이나 CROSS JOIN을 사용하면 된다.

-- 5-1
SELECT /*+ LEADING(T1) */
       COUNT (*)
  FROM t1
 INNER
  JOIN (SELECT LEVEL AS lv FROM DUAL CONNECT BY LEVEL <= 10)
    ON 1 = 1;

--------------------------------------------------------------------------------
| Id  | Operation                        | Name | Starts | A-Rows |   A-Time   |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |      |      1 |      1 |00:00:02.87 |
|   1 |  SORT AGGREGATE                  |      |      1 |      1 |00:00:02.87 |
|   2 |   MERGE JOIN CARTESIAN           |      |      1 |     10M|00:00:02.57 |
|   3 |    TABLE ACCESS FULL             | T1   |      1 |   1000K|00:00:00.31 |
|   4 |    BUFFER SORT                   |      |   1000K|     10M|00:00:01.20 |
|   5 |     VIEW                         |      |      1 |     10 |00:00:00.01 | -- !
|   6 |      CONNECT BY WITHOUT FILTERING|      |      1 |     10 |00:00:00.01 |
|   7 |       FAST DUAL                  |      |      1 |      1 |00:00:00.01 |
--------------------------------------------------------------------------------

-- 5-2
SELECT /*+ LEADING(T1) */
       COUNT (*)
  FROM t1
 CROSS
  JOIN (SELECT LEVEL AS lv FROM DUAL CONNECT BY LEVEL <= 10);

--------------------------------------------------------------------------------
| Id  | Operation                        | Name | Starts | A-Rows |   A-Time   |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |      |      1 |      1 |00:00:02.82 |
|   1 |  SORT AGGREGATE                  |      |      1 |      1 |00:00:02.82 |
|   2 |   MERGE JOIN CARTESIAN           |      |      1 |     10M|00:00:02.52 |
|   3 |    TABLE ACCESS FULL             | T1   |      1 |   1000K|00:00:00.30 |
|   4 |    BUFFER SORT                   |      |   1000K|     10M|00:00:01.18 |
|   5 |     VIEW                         |      |      1 |     10 |00:00:00.01 | -- !
|   6 |      CONNECT BY WITHOUT FILTERING|      |      1 |     10 |00:00:00.01 |
|   7 |       FAST DUAL                  |      |      1 |      1 |00:00:00.01 |
--------------------------------------------------------------------------------


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

POWER 함수의 CPU 연산 #1  (0) 2019.10.08
아우터 OR 조인 조건 #1  (0) 2019.10.06
중복 표현식  (0) 2019.10.02
병렬 쿼리 평균 수행 시간  (0) 2019.10.01
FBI 활용 사례  (0) 2019.09.25
Posted by 정희락_