<MINUS와 NOT EXISTS의 비교>
비교대상 | MINUS | NOT EXISTS | |
---|---|---|---|
수행 SQL | SELECT .... 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 성능에 가장 효율적인 방법을 선택하여 적용이 가능함 |
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)
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)
NOT EXISTS 주의 할 점
MINUS를 NOT EXISTS로 변경시 추출된 데이터 총 건수가 다를 수 있음.
NOT EXISTS는 SORT UNIQUE 오퍼레이션이 존재하지 않아 SELECT 절에 나열된 컬럼의 조합이 UNIQUE 하지 않다면,
NOT EXISTS를 이용한 SQL은 중복된 값이 추출될 가능성이 있으므로 주의해서 사용해야 함.
일반적으로는 PK컬럼이 SELECT절에 존재하여 문제가 발생하지 않으나 반드시 확인해야 함.
NOT EXISTS시 무조건 DISTINCT를 처리 하면 추가적인 정렬 작업이 발생하므로 체크하는 습관을 가져야 함
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)
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)
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)
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)