비효율적인 MINUS대신 NOT EXISTS를 사용하자.

<MINUS와 NOT EXISTS의 비교>

비교대상MINUSNOT EXISTS
수행 SQLSELECT ....
FROM A
MINUS
SELECT ...
FROM B
SELECT ...
FROM A
WHERE NOT EXISTS( SELECT ...
FROM B
WHERE B.XX = A.XX)
수행방식1. 테이블 A에서 데이터 추출
2. 추출된 데이터 SORT 연산
3. 테이블 B에서 데이터 추출
4. 추출된 데이터 SORT 연산
5. 2번과 4번 데이터 비교 후 최종 데이터 추출
1. 테이블 A에서 데이터 추출
2. 1번에서 추출한 데이터와 서브쿼리 테이블 B데이터와 존재 유무 체크 후 최종 데이터 추출
수행 순서고정(A->B)변경가능(A->B OR B->A)
테이블 수행 방식테이블 A와 상관없이 별도로 데이터 추출 후 SORT 연산 수행테이블 A의 추출 데이터를 이용한 인덱스 스캔 가능(조인 연결 키) 및 별도 수행도 가능
SQL 성능불리 : 1. 테이블 A, B에서 추출한 데이터를 SORT 연산 시 성능 저하
2. 테이블 A에서 추출한 데이터가 적고, 데이터 B에 아무런 조건이 없는 경우
FULL TABLE SCAN으로 처리하여야 하고, 추출된 데이터를 SORT 연산을 수행해야 함
유리 : 적절한 인덱스 스캔을 수행하거나 FULL TABLE SCAN을 수행하는 등
SQL 성능에 가장 효율적인 방법을 선택하여 적용이 가능함

테스트 수행

  • 추출 대상이 되는 데이터 집합의 WHERE 절에 효율적인 상수 조건이 있고, 비교 대상인 다른 집합에는 WHERE 절이 없는 경우

테스트1.MINUS


var b1 varchar2(10)
var b2 number
var b3 number

exec :b1 := 'A'
exec :b2 := 200000
exec :b3 := 300000

SELECT C1, C2, C3
  FROM SUBQUERY_T2
 WHERE C2 = :B1
   AND C1 >= :B2
   AND C1 <= :B3
MINUS
SELECT C4, C5, C6
  FROM SUBQUERY_T1

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.001          0          0          0          0
Execute      1    0.000        0.001          0          0          0          0
Fetch       21   11.357       11.608      37509      44145          0       1923
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total       23   11.357       11.611      37509      44145          0       1923

Misses in library cashe during parse   : 1
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
   1923  MINUS  (cr=44145 pr=37509 pw=0 time=512 us)
   3846   SORT UNIQUE (cr=275 pr=0 pw=0 time=384 us cost=276 size=46152 card=3846)
   3846    FILTER  (cr=275 pr=0 pw=0 time=6945 us)
   3846     TABLE ACCESS BY INDEX ROWID SUBQUERY_T2 (cr=275 pr=0 pw=0 time=5705 us cost=275 size=46152 card=3846)
   3846      INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=12 pr=0 pw=0 time=1240 us cost=12 size=0 card=3846)
 250000   SORT UNIQUE (cr=43870 pr=37509 pw=0 time=38894 us cost=83720 size=192000000 card=16000000)
16000000    TABLE ACCESS FULL SUBQUERY_T1 (cr=43870 pr=37509 pw=0 time=5179007 us cost=10329 size=192000000 card=16000000)


  • SUBQUERY_T1에는 조회 조건이 없어 FULL TABLE SCAN 수행,1,600만건에 대한 정렬 작업까지 수행
  • 테이블 사이즈가 작아 소요 시간이 오래 걸리진 않았지만, 사이즈가 크면 심각한 성능 저하 발생

테스트2.NOT EXISTS


SELECT C1, C2, C3
  FROM SUBQUERY_T2 T2
 WHERE C2 = :B1
   AND C1 >= :B2
   AND C1 <= :B3
   AND NOT EXISTS (SELECT /*+ UNNEST NL_AJ */
                          'X'
                     FROM SUBQUERY_T1 T1
                    WHERE T1.C4 = T2.C1
                      AND T1.C5 = T2.C2
                      AND T1.C6 = T2.C3)

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.001          0          0          0          0
Execute      1    0.000        0.004          0          0          0          0
Fetch       21    0.047        0.111        122       4311          0       1923
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total       23    0.047        0.115        122       4311          0       1923

Misses in library cashe during parse   : 1
Misses in library cashe during execute : 1
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
   1923  FILTER  (cr=4311 pr=122 pw=0 time=13454 us)
   1923   NESTED LOOPS ANTI (cr=4311 pr=122 pw=0 time=12428 us cost=32983 size=912 card=38)
   3846    TABLE ACCESS BY INDEX ROWID SUBQUERY_T2 (cr=314 pr=0 pw=0 time=9922 us cost=275 size=46152 card=3846)
   3846     INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=32 pr=0 pw=0 time=2976 us cost=12 size=0 card=3846)
   1923    TABLE ACCESS BY INDEX ROWID SUBQUERY_T1 (cr=3997 pr=122 pw=0 time=0 us cost=15 size=1476960 card=123080)
   1923     INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=3879 pr=0 pw=0 time=0 us cost=2 size=0 card=13)


  • SUBQUERY_T1이 INDEX SCAN으로 변경되고 정렬 작업이 제거. 성능 개선
앞 두 예제에서 MINUS SQL에서 발생하는 성능 문제
  • 비교 대상 테이블에서 데이터 춫ㄹ하는 방식이 비효율(FULL TABLE SCAN)임에도 불구하고 조회 조건이 없어 개선하기 힘듦
  • 비교 대상 테이블에서 많은 데이터가 추출되면 정렬 작업이 과다하게 발생

NOT EXISTS 주의 할 점

MINUS를 NOT EXISTS로 변경시 추출된 데이터 총 건수가 다를 수 있음.
NOT EXISTS는 SORT UNIQUE 오퍼레이션이 존재하지 않아 SELECT 절에 나열된 컬럼의 조합이 UNIQUE 하지 않다면,
NOT EXISTS를 이용한 SQL은 중복된 값이 추출될 가능성이 있으므로 주의해서 사용해야 함.
일반적으로는 PK컬럼이 SELECT절에 존재하여 문제가 발생하지 않으나 반드시 확인해야 함.
NOT EXISTS시 무조건 DISTINCT를 처리 하면 추가적인 정렬 작업이 발생하므로 체크하는 습관을 가져야 함

조인 대신 서브쿼리를 활용하자

  • 확인자 역할을 수행하는 테이블이란 FROM절에 나열된 테이블 중 SELECT절에 추출하는 컬럼 없이, WHERE 절에 조인 조건이나 FILTER 조건으로만 사용되는 테이블
  • 단순히 확인자로서의 역할을 수행하는 테이블에 대해서 조인 처리시, 종종 비효율이 발생할 수 있음

var b1 varchar2(10)
var b2 number
var b3 number

exec :b1 := 'A'
exec :b2 := 200000
exec :b3 := 300000

SELECT /*+ LEADING(T2 T1 T3) USE_NL(T2 T1 T3) */
       DISTINCT T2.C1, T2.C2, T3.C3
  FROM SUBQUERY_T2 T2,
       SUBQUERY_T1 T1,
       SUBQUERY_T3 T3
 WHERE T2.C2 = :B1
   AND T2.C1 >= :B2
   AND T2.C1 <= :B3
   AND T2.C1 = T1.C4
   AND T2.C2 = T1.C5
   AND T1.C4 = T3.C1
   AND T1.C5 = T3.C2

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.001          0          0          0          0
Execute      1    0.000        0.052          0          0          0          0
Fetch       21    0.374        0.391        133       6698          0       1923
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total       23    0.374        0.444        133       6698          0       1923

Misses in library cashe during parse   : 1
Misses in library cashe during execute : 1
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
   1923  HASH UNIQUE (cr=6698 pr=133 pw=0 time=768 us cost=14199 size=4992 card=192)
 123072   FILTER  (cr=6698 pr=133 pw=0 time=1607471 us)
 123072    NESTED LOOPS  (cr=6698 pr=133 pw=0 time=1590711 us)
 123072     NESTED LOOPS  (cr=6580 pr=133 pw=0 time=1440007 us cost=14198 size=4992 card=192)
 123072      NESTED LOOPS  (cr=4186 pr=0 pw=0 time=598979 us cost=4482 size=67984 card=4856)
   3846       INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=12 pr=0 pw=0 time=992 us cost=12 size=26922 card=3846)
 123072       INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=4174 pr=0 pw=0 time=42777 us cost=2 size=7 card=1)
 123072      INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=2394 pr=133 pw=0 time=0 us cost=1 size=0 card=1)
 123072     TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=118 pr=0 pw=0 time=0 us cost=2 size=12 card=1)


  • T2의 C1 컬럼은 UNIQUE 하나, T1의 C4는 UNIQUE 하지 않음.
  • T3의 C1/C3 컬럼은 UNIQUE
  • T2 테이블을 읽어 3,846건 추출
  • T1 테이블과 1:M 조인을 수행하여 123,072건으로 증가
  • T3 테이블과 조인하여 총 123,072건 추출. 최종 데이터는 DISTINCT하여 1,923건 추출
  • T1 테이블 조인 처리 시 불필요한 데이터 증가
  • T2 테이블과 T3 테이블을 먼저 조인 한 후 마지막으로 T1 테이블과 조인하도록 조인 순서를 변경하면 성능 개선 예상
  • T2와 T3의 조인조건이 없으므로 사용 불가.
  • T2와 T1 테이블 조인시 데이터가 급격히 증가한것으로 보아 T1과의 조인 시 중복 데이터가 많이 발생했을 것으로 예상.
  • T1은 데이터는 추출하지 않고, 조인 연결만 관여하는 확인자 역할을 하는 테이블이므로 존재 유무만 체크하여도 됨.
  • EXISTS를 이용하여 서브쿼리로 SQL을 변경
    {note}
    SUM/COUNT같은 그룹함수를 수행하는 경우는 데이터가 훼손될 수 있으므로 데이터 정합성 체크를 필히 수행햐 함.
    {note}

ELECT /*+ LEADING (T2 T1@SUB T3) USE_NL(T1 T2@SUB T3) */
       DISTINCT T2.C1, T2.C2, T3.C3
  FROM SUBQUERY_T2 T2,       
       SUBQUERY_T3 T3
 WHERE T2.C2 = :B1
   AND T2.C1 >= :B2
   AND T2.C1 <= :B3
   AND EXISTS (SELECT /*+ QB_NAME(SUB) */ 'X'
                 FROM SUBQUERY_T1 T1
                WHERE T2.C1 = T1.C4
                  AND T2.C2 = T1.C5
                  AND T1.C4 = T3.C1
                  AND T1.C5 = T3.C2)

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.000          0          0          0          0
Execute      1    0.000        0.000          0          0          0          0
Fetch       21    0.078        0.079          0       5692          0       1923
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total       23    0.078        0.079          0       5692          0       1923

Misses in library cashe during parse   : 0
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
   1923  HASH UNIQUE (cr=5692 pr=0 pw=0 time=1153 us cost=14199 size=2106 card=81)
   1923   FILTER  (cr=5692 pr=0 pw=0 time=75078 us)
   1923    NESTED LOOPS  (cr=5692 pr=0 pw=0 time=74717 us)
   1923     NESTED LOOPS  (cr=5574 pr=0 pw=0 time=71954 us cost=14198 size=2106 card=81)
   1923      NESTED LOOPS  (cr=4182 pr=0 pw=0 time=66068 us cost=4482 size=67984 card=4856)
   3846       INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=12 pr=0 pw=0 time=248 us cost=12 size=26922 card=3846)
   1923       SORT UNIQUE (cr=4170 pr=0 pw=0 time=0 us cost=2 size=7 card=1)
 123072        INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=4170 pr=0 pw=0 time=42084 us cost=2 size=7 card=1)
   1923      INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=1392 pr=0 pw=0 time=0 us cost=1 size=0 card=1)
   1923     TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=118 pr=0 pw=0 time=0 us cost=2 size=12 card=1)


  • T2테이블에서 추출된 3,846건에 대해 T1과 조인 시 1,923건으로 크게 줄었음
  • T3 테이블과의 조인도 1,923번만 조인되어 성능 개선

WHERE절의 서브쿼리를 조인으로 변경하자.

  • 서브쿼리가 한 개만 포함된 SQL일 경우, OPTMIZER는 하나의 서브쿼리에 COST를 계산하므로,
    통계 정보만 정확하다면 비교적 효율적인 실행계획 수립
  • WHERE절에 서브쿼리가 많다면, 모든 조합에 대한 COST를 계산하는 것이 아니라
    모든 서브쿼리를 UNNEST 수행한 것과 수행하지 않는 두 개의 COST 값만을 계산하므로
    최적의 실행계획 수립이 어려움.
  • SQL에 서브쿼리가 많다면, 효율적인 실행계획을 수립할 확률이 급격히 떨어짐

ALTER SYSTEM SET "_optim_peek_user_binds" = FALSE;

var b1 varchar2(10)
var b2 number
var b3 number
var b4 varchar2(10)
var b5 number
var b6 number
var b7 varchar2(10)
var b8 number
var b9 number

exec :b1 := 'A'
exec :b2 := 200000
exec :b3 := 100000
exec :b4 := '%'
exec :b5 := 100000
exec :b6 := 300000
exec :b7 := '%'
exec :b8 := 100000
exec :b9 := 300000

SELECT T1.*
  FROM SUBQUERY_T1 T1
 WHERE EXISTS (SELECT 'X'
          FROM SUBQUERY_T2 T2
         WHERE T2.C2 LIKE :B1
           AND T2.C3 >= :B2
           AND T2.C3 <= :B3
           AND T2.C1 = T1.C4
           AND T2.C2 = T1.C5)
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T3 T3
         WHERE T3.C2 LIKE :B4
           AND T3.C3 >= :B5
           AND T3.C3 <= :B6
           AND T3.C1 = T1.C4)
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T1 T11
         WHERE T11.C5 LIKE :B7
           AND T11.C4 >= :B8
           AND T11.C4 <= :B9
           AND T11.C4 = T1.C4)

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.002          0          0          0          0
Execute      1    0.016        0.009          0          0          0          0
Fetch        4    8.471      188.113      19035     437582          0        256
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total        6    8.486      188.124      19035     437582          0        256

Misses in library cashe during parse   : 1
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
    256  FILTER  (cr=437582 pr=19035 pw=0 time=183345 us)
    256   NESTED LOOPS SEMI (cr=437582 pr=19035 pw=0 time=182835 us cost=17 size=43 card=1)
 246144    NESTED LOOPS SEMI (cr=434790 pr=19035 pw=0 time=286116480 us cost=15 size=31 card=1)
 246144     NESTED LOOPS  (cr=427094 pr=19035 pw=0 time=279875072 us cost=13 size=24 card=1)
 100002      SORT UNIQUE (cr=974 pr=371 pw=0 time=155883 us cost=9 size=12 card=1)
 100002       TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=974 pr=371 pw=0 time=166881 us cost=9 size=12 card=1)
 200001        INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=504 pr=371 pw=0 time=128342 us cost=8 size=0 card=87)
 246144      TABLE ACCESS BY INDEX ROWID SUBQUERY_T1 (cr=426120 pr=18664 pw=0 time=142582224 us cost=3 size=12 card=1)
 246144       INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=176999 pr=0 pw=0 time=722862 us cost=2 size=0 card=1)
   3846     INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=7696 pr=0 pw=0 time=0 us cost=2 size=14000 card=2000)
      4    TABLE ACCESS BY INDEX ROWID SUBQUERY_T2 (cr=2792 pr=0 pw=0 time=0 us cost=2 size=12 card=1)
   3846     INDEX UNIQUE SCAN PK_SUBQUERY_2 (cr=2526 pr=0 pw=0 time=0 us cost=1 size=0 card=1)



  • OPTIMIZER는 SUBQUERY_T3 테이블을 먼저 읽고 수행하는 것이 효율적이라고 잘못 판단하여 비효율이 발생
  • 사용자가 의도한 대로 SUBQUERY_T2가 먼저 읽고 수행되도록 유도
  • 이런 경우 개선방법으로 서브쿼리를 조인으로 변경

SELECT /*+ LEADING(T2 T1) USE_NL(T2 T1) */
  T1.*
  FROM SUBQUERY_T1 T1,
       (SELECT DISTINCT T2.C1,
               T2.C2
          FROM SUBQUERY_T2 T2
         WHERE T2.C2 LIKE :B1
           AND T2.C3 >= :B2
           AND T2.C3 <= :B3) T2
 WHERE T2.C1 = T1.C4
   AND T2.C2 = T1.C5
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T3 T3
         WHERE T3.C2 LIKE :B4
           AND T3.C3 >= :B5
           AND T3.C3 <= :B6
           AND T3.C1 = T1.C4)
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T1 T11
         WHERE T11.C5 LIKE :B7
           AND T11.C4 >= :B8
           AND T11.C4 <= :B9
           AND T11.C4 = T1.C4)

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.000          0          0          0          0
Execute      1    0.000        0.000          0          0          0          0
Fetch        4    0.062        0.074          0       1248          0        256
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total        6    0.062        0.075          0       1248          0        256

Misses in library cashe during parse   : 0
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
    256  FILTER  (cr=1248 pr=0 pw=0 time=56100 us)
    256   NESTED LOOPS SEMI (cr=1248 pr=0 pw=0 time=55845 us cost=21 size=43 card=1)
    256    NESTED LOOPS SEMI (cr=1234 pr=0 pw=0 time=32895 us cost=18 size=31 card=1)
    256     NESTED LOOPS  (cr=1223 pr=0 pw=0 time=28560 us cost=16 size=24 card=1)
      4      TABLE ACCESS BY INDEX ROWID SUBQUERY_T2 (cr=952 pr=0 pw=0 time=276 us cost=13 size=12 card=1)
 200001       INDEX RANGE SCAN PK_SUBQUERY_2 (cr=423 pr=0 pw=0 time=32117 us cost=7 size=0 card=2250)
    256      TABLE ACCESS BY INDEX ROWID SUBQUERY_T1 (cr=271 pr=0 pw=0 time=7623 us cost=3 size=12 card=1)
    256       INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=15 pr=0 pw=0 time=4977 us cost=2 size=0 card=1)
      4     INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=11 pr=0 pw=0 time=0 us cost=2 size=14000 card=2000)
      4    TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=14 pr=0 pw=0 time=0 us cost=3 size=12 card=1)
      4     INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=11 pr=0 pw=0 time=0 us cost=2 size=0 card=1)

- 실제로는 PK_SUBQUERY_2 를 탔음

SELECT /*+ LEADING(T2 T1) USE_NL(T2 T1) INDEX(T2 SUBQUERY_T2_IDX_01) */
  T1.*
  FROM SUBQUERY_T1 T1,
       (SELECT DISTINCT T2.C1,
               T2.C2
          FROM SUBQUERY_T2 T2
         WHERE T2.C2 LIKE :B1
           AND T2.C3 >= :B2
           AND T2.C3 <= :B3) T2
 WHERE T2.C1 = T1.C4
   AND T2.C2 = T1.C5
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T3 T3
         WHERE T3.C2 LIKE :B4
           AND T3.C3 >= :B5
           AND T3.C3 <= :B6
           AND T3.C1 = T1.C4)
   AND EXISTS (SELECT 'X'
          FROM SUBQUERY_T1 T11
         WHERE T11.C5 LIKE :B7
           AND T11.C4 >= :B8
           AND T11.C4 <= :B9
           AND T11.C4 = T1.C4)

Call     Count CPU Time Elapsed Time       Disk      Query    Current       Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse        1    0.000        0.000          0          0          0          0
Execute      1    0.000        0.000          0          0          0          0
Fetch        4    0.016        0.017          0        869          0        256
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total        6    0.016        0.018          0        869          0        256

Misses in library cashe during parse   : 0
Optimizer Goal : ALL_ROWS
Parsing user : SCOTT (ID=84)


Rows     Row Source Operation
-------  -----------------------------------------------------------------------
    256  FILTER  (cr=869 pr=0 pw=0 time=22695 us)
    256   NESTED LOOPS SEMI (cr=869 pr=0 pw=0 time=22695 us cost=65 size=43 card=1)
    256    NESTED LOOPS SEMI (cr=855 pr=0 pw=0 time=17212 us cost=62 size=31 card=1)
    256     NESTED LOOPS  (cr=844 pr=0 pw=0 time=14535 us cost=60 size=24 card=1)
      4      TABLE ACCESS BY INDEX ROWID SUBQUERY_T2 (cr=573 pr=0 pw=0 time=36 us cost=57 size=12 card=1)
   7692       INDEX RANGE SCAN SUBQUERY_T2_IDX_01 (cr=45 pr=0 pw=0 time=8699 us cost=51 size=0 card=87)
    256      TABLE ACCESS BY INDEX ROWID SUBQUERY_T1 (cr=271 pr=0 pw=0 time=5166 us cost=3 size=12 card=1)
    256       INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=15 pr=0 pw=0 time=3780 us cost=2 size=0 card=1)
      4     INDEX RANGE SCAN SUBQUERY_T1_IDX1_01 (cr=11 pr=0 pw=0 time=0 us cost=2 size=14000 card=2000)
      4    TABLE ACCESS BY INDEX ROWID SUBQUERY_T3 (cr=14 pr=0 pw=0 time=0 us cost=3 size=12 card=1)
      4     INDEX RANGE SCAN SUBQUERY_T3_IDX_01 (cr=11 pr=0 pw=0 time=0 us cost=2 size=0 card=1)

  • WHERE절에 많은 서브쿼리가 포함될 경우, 성능이 좋지 않은 실행계획이 수립할 확률이매우 높으므로
    서브쿼리를 조인으로 변경하는 것을 검토해야 함