oracle에서 not exists 대외 검색의 영향 설명
최근 동료가 발견한 문제는 12c에서 뛰는 버퍼 get은 높지만 10g에서 뛰는 버퍼는 낮다는 것이다.12c의 최적화기에 문제가 있는지 의심스럽다.
이 10g의 환경과 12c의 환경은 데이터 양이 대체적으로 같고 아주 적은 부분만 다르다. 그러나 이 아주 적은 부분이 다르기 때문에 not exists의 하위 조회가 서로 다른 값을 되돌려주고 외부 조회에 서로 다른 영향을 미친다.
우리는 다음과 같은 코드로 시뮬레이션을 해 봅시다.
초기화 데이터:
--10g
drop table t1;
drop table t2;
create table t1 (id number,name varchar2(20),dep_id varchar2(10));
create table t2 (id number,name varchar2(20),dep_id varchar2(10));
insert into t1 select rownum,'a','kk' from dual connect by level <=3000000;
insert into t2 select rownum,'a','kk' from dual connect by level <=1000000;
insert into t2 select rownum,'a','mm' from dual;
commit;
--12c
drop table t1;
drop table t2;
create table t1 (id number,name varchar2(20),dep_id varchar2(10));
create table t2 (id number,name varchar2(20),dep_id varchar2(10));
insert into t1 select rownum,'a','kk' from dual connect by level <=3000000;
insert into t2 select rownum,'a','kk' from dual connect by level <=1000000;
commit;
우리가 보기에 12c의 데이터와 10g은 아주 적은 차이일 뿐이다. t1표 12c와 10g은 모두 같고 t2표는 12c에서 한 줄의 데이터만 적다.
--10g
SQL> select dep_id,count(*) from t1 group by dep_id;
DEP_ID COUNT(*)
-------------------- ----------
kk 3000000
SQL> select dep_id,count(*) from t2 group by dep_id;
DEP_ID COUNT(*)
-------------------- ----------
mm 1
kk 1000000
SQL>
--12c
SQL> select dep_id,count(*) from t1 group by dep_id;
DEP_ID COUNT(*)
-------------------- ----------
kk 3000000
SQL> select dep_id,count(*) from t2 group by dep_id;
DEP_ID COUNT(*)
-------------------- ----------
kk 1000000
SQL>
우리가 실행할 sql 문장은 다음과 같습니다.
select count(*)
from t1, t2
where t1.id = t2.id
and t1.dep_id = 'kk'
and not exists (select 1
from t1, t2
where t1.id = t2.id
and t2.dep_id = 'mm');
우선 실행 상황의 차이를 살펴보자. 10g의 버퍼링은 작고 12c가 많다.
--10g
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm');
COUNT(*)
----------
0
SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 22t5mb43w55pr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not
exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm')
Plan hash value: 3404612428
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.02 | 2086 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.02 | 2086 | | | |
|* 2 | FILTER | | 1 | | 0 |00:00:00.02 | 2086 | | | |
|* 3 | HASH JOIN | | 0 | 901K| 0 |00:00:00.01 | 0 | 39M| 5518K| |
| 4 | TABLE ACCESS FULL| T2 | 0 | 901K| 0 |00:00:00.01 | 0 | | | |
|* 5 | TABLE ACCESS FULL| T1 | 0 | 2555K| 0 |00:00:00.01 | 0 | | | |
|* 6 | HASH JOIN | | 1 | 23 | 1 |00:00:00.02 | 2086 | 1517K| 1517K| 612K (0)|
|* 7 | TABLE ACCESS FULL| T2 | 1 | 23 | 1 |00:00:00.02 | 2082 | | | |
| 8 | TABLE ACCESS FULL| T1 | 1 | 2555K| 1 |00:00:00.01 | 4 | | | |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( IS NULL)
3 - access("T1"."ID"="T2"."ID")
5 - filter("T1"."DEP_ID"='kk')
6 - access("T1"."ID"="T2"."ID")
7 - filter("T2"."DEP_ID"='mm')
Note
-----
- dynamic sampling used for this statement
34 rows selected.
SQL>
--12c
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm');
COUNT(*)
----------
1000000
SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 22t5mb43w55pr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where
t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2
where t1.id=t2.id and t2.dep_id='mm')
Plan hash value: 1692274438
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.79 | 10662 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.79 | 10662 | | | |
|* 2 | FILTER | | 1 | | 1000K|00:00:00.74 | 10662 | | | |
|* 3 | HASH JOIN | | 1 | 1215K| 1000K|00:00:00.52 | 8579 | 43M| 6111K| 42M (0)|
| 4 | TABLE ACCESS FULL | T2 | 1 | 1215K| 1000K|00:00:00.01 | 2083 | | | |
|* 5 | TABLE ACCESS FULL | T1 | 1 | 2738K| 3000K|00:00:00.07 | 6496 | | | |
|* 6 | HASH JOIN RIGHT SEMI| | 1 | 35 | 0 |00:00:00.02 | 2083 | 1245K| 1245K| 461K (0)|
|* 7 | TABLE ACCESS FULL | T2 | 1 | 23 | 0 |00:00:00.02 | 2083 | | | |
| 8 | TABLE ACCESS FULL | T1 | 0 | 2738K| 0 |00:00:00.01 | 0 | | | |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( IS NULL)
3 - access("T1"."ID"="T2"."ID")
5 - filter("T1"."DEP_ID"='kk')
6 - access("T1"."ID"="T2"."ID")
7 - filter("T2"."DEP_ID"='mm')
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
35 rows selected.
SQL>
SQL>
23, 24줄을 볼 수 있는데 10g에서 실행할 때 버퍼스는 0이고 12c에서 78, 79줄에서 버퍼는 2083+6496이다.
즉, 10g에서 외계 조회는 t1과 t2의 스캐닝을 하지 않고 결과를 직접 되돌려주고, 12c에서 외계 조회는 t1표와 t2 표층 스캐닝을 해야 결과를 되돌려준다.
이것은 사실 10g과 12c의 차이가 아니라 not exists의 반환 데이터가 외부에 미치는 영향이다.하위 쿼리는 0줄 기록을 되돌려야 not exist의 조건을 충족시켜 외부 쿼리 결과를 되돌려줍니다.
10g에서 하위 조회가 한 줄의 기록을 되돌려주었다
--10g
SQL> select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm';
1
----------
1
SQL>
not exists(즉 0줄이 만족해야 함)가 충족되지 않기 때문에 외부에서 계속 조회할 필요가 없습니다.기록 0 줄을 직접 되돌려줍니다.
12c에서 하위 조회는 0줄 기록을 되돌려주고 not exist의 조건을 충족시키기 때문에 외부 조회에서 계속 조회해야 한다.
--12c
SQL> select count(*) from t1,t2 where t1.id=t2.id and t2.dep_id='kk';
COUNT(*)
----------
1000000
SQL> set line 1000
SQL> set pages 1000
SQL> col PLAN_TABLE_OUTPUT for a250
SQL>
SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='kk');
COUNT(*)
----------
0
SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID c5hj2p2jt1fxf, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where
t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2
where t1.id=t2.id and t2.dep_id='kk')
Plan hash value: 1692274438
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.28 | 2087 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.28 | 2087 | | | |
|* 2 | FILTER | | 1 | | 0 |00:00:00.28 | 2087 | | | |
|* 3 | HASH JOIN | | 0 | 1215K| 0 |00:00:00.01 | 0 | 69M| 7428K| |
| 4 | TABLE ACCESS FULL | T2 | 0 | 1215K| 0 |00:00:00.01 | 0 | | | |
|* 5 | TABLE ACCESS FULL | T1 | 0 | 2738K| 0 |00:00:00.01 | 0 | | | |
|* 6 | HASH JOIN RIGHT SEMI| | 1 | 2738K| 1 |00:00:00.28 | 2087 | 43M| 6111K| 42M (0)|
|* 7 | TABLE ACCESS FULL | T2 | 1 | 1215K| 1000K|00:00:00.12 | 2083 | | | |
| 8 | TABLE ACCESS FULL | T1 | 1 | 2738K| 1 |00:00:00.01 | 4 | | | |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( IS NULL)
3 - access("T1"."ID"="T2"."ID")
5 - filter("T1"."DEP_ID"='kk')
6 - access("T1"."ID"="T2"."ID")
7 - filter("T2"."DEP_ID"='kk')
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
35 rows selected.
SQL>
38, 39 줄의 버퍼가 0인 것을 볼 수 있습니다.
총결산
이상은 이 글의 전체 내용입니다. 본고의 내용이 여러분의 학습이나 업무에 어느 정도 도움을 줄 수 있기를 바랍니다. 의문이 있으면 댓글로 의사소통을 할 수 있습니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.