프린트 하기

OS환경 : Oracle Linux 7.6 (64bit)

 

DB 환경 : Oracle Database 19.11.0.0

 

방법 : 오라클 19c 실행계획 비교 DBMS_XPLAN.COMPARE_PLANS 

오라클 19c New feature 인 DBMS_XPLAN.COMPARE_PLANS 을 이용해서 실행계획을 비교해볼수 있음

인덱스 사용 유무도 확인을 할수 있고 본문에서 테스트할 QT(Query Transformation)에 대해서도 비교가 가능함

 

 

기본적인 사용방법

1
2
3
4
5
6
7
8
9
10
11
12
13
SQL> 
VARIABLE v_rep CLOB
BEGIN
:v_rep := DBMS_XPLAN.COMPARE_PLANS(
reference_plan => cursor_cache_object('db26h2x1zp29w'null),
compare_plan_list =>
plan_object_list(cursor_cache_object('2rq3vgb7sdshm'null)),
type => 'TEXT',
level => 'TYPICAL',
section => 'ALL');
END;
/
PL/SQL procedure successfully completed.

reference_plan 와 compare_plan_list 에 비교할 sql의 sql_id를 입력해주면됨

동일 쿼리에 대해 플랜이 여러개인 경우

https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/generating-and-displaying-execution-plans.html#GUID-7909832F-F81B-40CC-AB8A-7F52CA84F104 와 같이 

dba_sql_plan_baselines에 있는 sql_handle, plan_name (SPM_OBJECT 인자)를 넣어 비교할수도있음

type 인자는 text, html, xml로 세가지 방법으로 출력할수 있음

level 인자는 basic, typical, all 세가지가 있는데 all 이 제일 많은 정보를 출력함

basic 은 Plan은 나오지만 Plan Operation에서 rows 와 Bytes 등 값이 나오지 않음

typical 은 Plan Operation에서 rows 와 Bytes 등 값이 나옴, Predicate Information 이 나옴

all 은 typical 내용을 모두 포함하고 추가로 Hint Report도 포함됨

(https://positivemh.tistory.com/775 에서 설명한 내용)

 

 

세가지 테스트를 진행함

각각 샘플쿼리 실행 후 dbms_xplan.display_cursor으로 실행계획과 sql_id를 확인하고 dbms_xplan.compare_plans로 비교함

1. SJ(Semi Join) 테스트(where 절 서브쿼리를 Semi 조인으로 변환)

2. OE(Outer join table Elimination) 테스트(불필요한 outer 쪽 테이블 제거)

3. GBP(Group By Placement) 테스트(Group By를 먼저 수행하고 Join)

 

 

테스트1. SJ(Semi Join) 테스트

SJ 발생시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
SQL>
select /*+ gather_plan_statistics */
deptno, dname, loc
from dept d
where exists ( select 1
               from emp e
               where e.deptno = d.deptno);
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    3qb3cd6b389qw, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ deptno, dname, loc from dept d
where exists ( select 1            from emp e         where
e.deptno = d.deptno)
 
Plan hash value: 1754319153
 
---------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |     A-Time   | Buffers | Reads  |    OMem |    1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |        |        |      5 (100)|        |       3 |00:00:00.01 |      9 |       8 |         |         |        |
|*  1 |  HASH JOIN SEMI    |      |     1 |      3 |     69 |      5   (0)| 00:00:01 |       3 |00:00:00.01 |      9 |       8 |    1399K|    1399K| 1117K (0)|
|   2 |   TABLE ACCESS FULL| DEPT |     1 |      4 |     80 |      2   (0)| 00:00:01 |       4 |00:00:00.01 |      2 |       2 |         |         |        |
|   3 |   TABLE ACCESS FULL| EMP  |     1 |     14 |     42 |      3   (0)| 00:00:01 |      14 |00:00:00.01 |      7 |       6 |         |         |        |
---------------------------------------------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / D@SEL$1
   3 - SEL$5DA710D3 / E@SEL$2
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$5DA710D3")
      UNNEST(@"SEL$2")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      FULL(@"SEL$5DA710D3" "D"@"SEL$1")
      FULL(@"SEL$5DA710D3" "E"@"SEL$2")
      LEADING(@"SEL$5DA710D3" "D"@"SEL$1" "E"@"SEL$2")
      USE_HASH(@"SEL$5DA710D3" "E"@"SEL$2")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("E"."DEPTNO"="D"."DEPTNO")
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=1; rowset=256"D"."DEPTNO"[NUMBER,22], "LOC"[VARCHAR2,13], "DNAME"[VARCHAR2,14]
   2 - (rowset=256"DEPTNO"[NUMBER,22], "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]
   3 - (rowset=256"E"."DEPTNO"[NUMBER,22]
 
Query Block Registry:
---------------------
 
  <q o="2"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[SEL$1]]></s></h></f></q>
  <q o="2"><n><![CDATA[SEL$2]]></n><f><h><t><![CDATA[E]]></t><s><![CDATA[SEL$2]]></s></h></f></q>
  <q o="19" f="y" h="y"><n><![CDATA[SEL$5DA710D3]]></n><p><![CDATA[SEL$1]]></p><i><o><t>SQ</t><v><![CDATA[SEL$2]]></v></o></i><f><h><t><![CDATA[D]]
    ></t><s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[E]]></t><s><![CDATA[SEL$2]]></s></h></f></q>
 
 
 
65 rows selected.

22번째 줄 : exists 서브쿼리가 unnesting 되어 semi join으로 풀림

 

 

SJ 미발생시

(_always_semi_join 파라미터를 Off로 변경하여 semi join이 발생하지 않게끔함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
SQL>
select /*+ gather_plan_statistics opt_param('_always_semi_join','Off') */
deptno, dname, loc
from dept d
where exists ( select 1
               from emp e
               where e.deptno = d.deptno);
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    aadj5zdnng547, child number 0
-------------------------------------
select /*+ gather_plan_statistics opt_param('_always_semi_join','Off')
*/ deptno, dname, loc from dept d where exists ( select 1
 from emp e           where e.deptno = d.deptno)
 
Plan hash value: 3547749009
 
---------------------------------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |     A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |        |        |      6 (100)|        |       3 |00:00:00.01 |     28 |
|*  1 |  FILTER        |      |     1 |        |        |         |        |       3 |00:00:00.01 |     28 |
|   2 |   TABLE ACCESS FULL| DEPT |     1 |      4 |     80 |      2   (0)| 00:00:01 |       4 |00:00:00.01 |      4 |
|*  3 |   TABLE ACCESS FULL| EMP  |     4 |      2 |      6 |      2   (0)| 00:00:01 |       3 |00:00:00.01 |     24 |
---------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   2 - SEL$1 / D@SEL$1
   3 - SEL$2 / E@SEL$2
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      OPT_PARAM('_always_semi_join' 'off')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2")
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "D"@"SEL$1")
      PQ_FILTER(@"SEL$1" SERIAL)
      FULL(@"SEL$2" "E"@"SEL$2")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter( IS NOT NULL)
   3 - filter("E"."DEPTNO"=:B1)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "DEPTNO"[NUMBER,22], "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]
   2 - "DEPTNO"[NUMBER,22], "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
 
   0 -    STATEMENT
       -  opt_param('_always_semi_join','Off')
 
Query Block Registry:
---------------------
 
  <q o="2" f="y"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[SEL$1]]></s></h></f></q>
  <q o="2" f="y"><n><![CDATA[SEL$2]]></n><f><h><t><![CDATA[E]]></t><s><![CDATA[SEL$2]]></s></h></f></q>
 
 
 
68 rows selected.

24번째 줄 : exists 서브쿼리가 unnesting 되지 않고 Filter로 풀림

 

 

COMPARE_PLANS 비교

reference_plan 에 기존 플랜, compare_plan_list 에 비교 대상 플랜을 넣어 비교함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
SQL> 
VARIABLE v_rep CLOB
BEGIN
:v_rep := DBMS_XPLAN.COMPARE_PLANS(
reference_plan => cursor_cache_object('3qb3cd6b389qw'null),
compare_plan_list =>
plan_object_list(cursor_cache_object('aadj5zdnng547'null)),
type => 'TEXT',
level => 'TYPICAL',
section => 'ALL');
END;
/
 
PL/SQL procedure successfully completed.
 
SQL> 
set pages 50000
set long 100000
set lines 210
col report for a200
SELECT :v_rep REPORT FROM DUAL;
 
REPORT
-----------------------------------------------------------------------------------------------
 
COMPARE PLANS REPORT
---------------------------------------------------------------------------------------------
  Current user         : IMSI
  Total number of plans  : 2
  Number of findings     : 1
---------------------------------------------------------------------------------------------
 
COMPARISON DETAILS
---------------------------------------------------------------------------------------------
 Plan Number        : 1 (Reference Plan)
 Plan Found        : Yes
 Plan Source        : Cursor Cache
 SQL ID         : 3qb3cd6b389qw
 Child Number        : 0
 Plan Database Version    : 19.0.0.0
 Parsing Schema     : "IMSI"
 SQL Text        : select /*+ gather_plan_statistics */ deptno, dname,
            loc from dept d where exists ( select 1 from emp e
            where e.deptno = d.deptno)
 
Plan
-----------------------------
 
 Plan Hash Value  : 1754319153
 
----------------------------------------------------------------------
| Id  | Operation         | Name | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |       |       |    5 |         |
| * 1 |   HASH JOIN SEMI     |        |     3 |    69 |    5 | 00:00:01 |
|   2 |    TABLE ACCESS FULL | DEPT |     4 |    80 |    2 | 00:00:01 |
|   3 |    TABLE ACCESS FULL | EMP  |    14 |    42 |    3 | 00:00:01 |
----------------------------------------------------------------------
 
Predicate Information (identified by operation id):
------------------------------------------
* 1 - access("E"."DEPTNO"="D"."DEPTNO")
 
---------------------------------------------------------------------------------------------
 Plan Number        : 2
 Plan Found        : Yes
 Plan Source        : Cursor Cache
 SQL ID         : aadj5zdnng547
 Child Number        : 0
 Plan Database Version    : 19.0.0.0
 Parsing Schema     : "IMSI"
 SQL Text        : select /*+ gather_plan_statistics
            opt_param('_always_semi_join','Off') */ deptno, dname,
            loc from dept d where exists ( select 1 from emp e
            where e.deptno = d.deptno)
 
Plan
-----------------------------
 
 Plan Hash Value  : 3547749009
 
----------------------------------------------------------------------
| Id  | Operation         | Name | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |       |       |    6 |         |
| * 1 |   FILTER         |        |       |       |      |         |
|   2 |    TABLE ACCESS FULL | DEPT |     4 |    80 |    2 | 00:00:01 |
| * 3 |    TABLE ACCESS FULL | EMP  |     2 |     6 |    2 | 00:00:01 |
----------------------------------------------------------------------
 
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter( IS NOT NULL)
* 3 - filter("E"."DEPTNO"=:B1)
 
 
Comparison Results (1):
-----------------------------
 1. Query block SEL$1: Transformation SUBQUERY UNNEST occurred only in the
    reference plan (result query block: SEL$5DA710D3).
 
 
---------------------------------------------------------------------------------------------

97번째 줄 : Comparison Results에 reference plan(기존플랜)에서만 SUBQUERY UNNEST Transformation 이 발생했다고 나옴

 

 

테스트2. OE(Outer join table Elimination) 테스트(불필요한 outer 쪽 테이블 제거)

OE 발생시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
SQL>
select /*+ gather_plan_statistics */
e.empno, e.ename, e.job
from emp e, dept d
where e.deptno = d.deptno(+);
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    5rrubfj00hhnw, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ e.empno, e.ename, e.job from emp
e, dept d where e.deptno = d.deptno(+)
 
Plan hash value: 3956160932
 
--------------------------------------------------------------------------------------------------------------------
| Id  | Operation      | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |    A-Time     | Buffers |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |     |    1 |       |       |     3 (100)|       |     14 |00:00:00.01 |     7 |
|   1 |  TABLE ACCESS FULL| EMP  |    1 |    14 |   252 |     3   (0)| 00:00:01 |     14 |00:00:00.01 |     7 |
--------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$F7859CDE / E@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$F7859CDE")
      ELIMINATE_JOIN(@"SEL$1" "D"@"SEL$1")
      OUTLINE(@"SEL$1")
      FULL(@"SEL$F7859CDE" "E"@"SEL$1")
      END_OUTLINE_DATA
  */
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9]
 
Query Block Registry:
---------------------
 
  <q o="34" f="y" h="y"><n><![CDATA[SEL$F7859CDE]]></n><p><![CDATA[SEL$1]]></p><i><o><t>TA</t><v><![CDATA[D@SE
    L$1]]></v></o></i><f><h><t><![CDATA[E]]></t><s><![CDATA[SEL$1]]></s></h></f></q>
  <q o="2"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[E]]></
    t><s><![CDATA[SEL$1]]></s></h></f></q>
 
 
 
49 rows selected.

21번째 줄 : OE(Outer join table Elimination) 가 발생하여 불필요한 outer 쪽 테이블(dept)이 제거됨

 

 

OE 미발생시

(_optimizer_join_elimination_enabled 파라미터를 false로 변경하여 oe가 발생하지 않게끔함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
SQL>
select /*+ gather_plan_statistics opt_param('_optimizer_join_elimination_enabled','false') */
e.empno, e.ename, e.job
from emp e, dept d
where e.deptno = d.deptno(+);
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    5ffq63tnfmpq3, child number 0
-------------------------------------
select /*+ gather_plan_statistics opt_param('_optimizer_join_elimination
_enabled','false') */ e.empno, e.ename, e.job from emp e, dept d where
e.deptno = d.deptno(+)
 
Plan hash value: 438843259
 
---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation       | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads    |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |        1 |        |       |     3 (100)|           |     14 |00:00:00.01 |        11 |      1 |
|   1 |  NESTED LOOPS OUTER|         |        1 |     14 |   336 |     3     (0)| 00:00:01 |     14 |00:00:00.01 |        11 |      1 |
|   2 |   TABLE ACCESS FULL| EMP     |        1 |     14 |   294 |     3     (0)| 00:00:01 |     14 |00:00:00.01 |         7 |      0 |
|*  3 |   INDEX UNIQUE SCAN| PK_DEPT |       14 |      1 |     3 |     0     (0)|           |     14 |00:00:00.01 |         4 |      1 |
---------------------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   2 - SEL$1 / E@SEL$1
   3 - SEL$1 / D@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      OPT_PARAM('_optimizer_join_elimination_enabled' 'false')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "E"@"SEL$1")
      INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      LEADING(@"SEL$1" "E"@"SEL$1" "D"@"SEL$1")
      USE_NL(@"SEL$1" "D"@"SEL$1")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("E"."DEPTNO"="D"."DEPTNO")
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9]
   2 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9], "E"."DEPTNO"[NUMBER,22]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
 
   0 -    STATEMENT
       -  opt_param('_optimizer_join_elimination_enabled','false')
 
Query Block Registry:
---------------------
 
  <q o="2" f="y"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[SEL$1]]></s></h><h><t><![CDATA[E]]></t><s><!
    [CDATA[SEL$1]]></s></h></f></q>

 
 
67 rows selected.

24번째 줄 : OE(Outer join table Elimination) 가 발생하지 못해 불필요한 outer 쪽 테이블(dept)이 존재함

 

 

COMPARE_PLANS 비교

reference_plan 에 기존 플랜, compare_plan_list 에 비교 대상 플랜을 넣어 비교함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
SQL> 
VARIABLE v_rep CLOB
BEGIN
:v_rep := DBMS_XPLAN.COMPARE_PLANS(
reference_plan => cursor_cache_object('5rrubfj00hhnw'null),
compare_plan_list =>
plan_object_list(cursor_cache_object('5ffq63tnfmpq3'null)),
type => 'TEXT',
level => 'TYPICAL',
section => 'ALL');
END;
/
 
PL/SQL procedure successfully completed.
 
SQL> 
set pages 50000
set long 100000
set lines 210
col report for a200
SELECT :v_rep REPORT FROM DUAL;
REPORT
---------------------------------------------------------------------------------------------
 
COMPARE PLANS REPORT
---------------------------------------------------------------------------------------------
  Current user         : IMSI
  Total number of plans  : 2
  Number of findings     : 1
---------------------------------------------------------------------------------------------
 
COMPARISON DETAILS
---------------------------------------------------------------------------------------------
 Plan Number        : 1 (Reference Plan)
 Plan Found        : Yes
 Plan Source        : Cursor Cache
 SQL ID         : 5rrubfj00hhnw
 Child Number        : 0
 Plan Database Version    : 19.0.0.0
 Parsing Schema     : "IMSI"
 SQL Text        : select /*+ gather_plan_statistics */ e.empno,
            e.ename, e.job from emp e, dept d where e.deptno =
            d.deptno(+)
 
Plan
-----------------------------
 
 Plan Hash Value  : 3956160932
 
--------------------------------------------------------------------
| Id | Operation       | Name | Rows | Bytes | Cost | Time       |
--------------------------------------------------------------------
|  0 | SELECT STATEMENT    |      |     |     |    3 |       |
|  1 |     TABLE ACCESS FULL | EMP  |   14 |   252 |    3 | 00:00:01 |
--------------------------------------------------------------------
---------------------------------------------------------------------------------------------
 Plan Number          : 2
 Plan Found          : Yes
 Plan Source          : Cursor Cache
 SQL ID           : 5ffq63tnfmpq3
 Child Number          : 0
 Plan Database          : 19.0.0.0
 Version
 Parsing Schema       : "IMSI"
 SQL Text          : select /*+ gather_plan_statistics
              opt_param('_optimizer_join_elimination_enabled','false')
              */ e.empno, e.ename, e.job from emp e, dept d where
              e.deptno = d.deptno(+)
 
Plan
-----------------------------
 
 Plan Hash Value  : 438843259
 
-------------------------------------------------------------------------
| Id  | Operation         | Name    | Rows | Bytes | Cost | Time    |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |      |       |    3 |        |
|   1 |   NESTED LOOPS OUTER |           |   14 |   336 |    3 | 00:00:01 |
|   2 |    TABLE ACCESS FULL | EMP     |   14 |   294 |    3 | 00:00:01 |
| * 3 |    INDEX UNIQUE SCAN | PK_DEPT |    1 |     3 |    0 |        |
-------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
------------------------------------------
* 3 - access("E"."DEPTNO"="D"."DEPTNO")
 
 
Comparison Results (1):
-----------------------------
 1. Query block SEL$1: Transformation JOIN REMOVED FROM QUERY BLOCK occurred
    only in the reference plan (result query block: SEL$F7859CDE).
 
 
---------------------------------------------------------------------------------------------

89번째 줄 : Comparison Results에 reference plan(기존플랜)에서만 JOIN REMOVED FROM QUERY BLOCK Transformation 이 발생했다고 나옴

 

 

테스트3. GBP(Group By Placement) 테스트(Group By를 먼저 수행하고 Join)

GBP 발생시

(GBP를 발생시키기 위해  place_group_by 힌트를 사용함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
SQL>
select /*+ gather_plan_statistics qb_name(main) place_group_by(@main(d@main)) */
e.job, sum(sal)
from emp e, dept d
where e.deptno = d.deptno
and d.dname in('ACCOUNTING''SALES')
group by job;
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    db26h2x1zp29w, child number 0
-------------------------------------
select /*+ gather_plan_statistics qb_name(main)
place_group_by(@main(d@main)) */ e.job, sum(sal) from emp e, dept d
where e.deptno = d.deptno and d.dname in('ACCOUNTING''SALES') group
by job
 
Plan hash value: 2777097701
 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |    OMem |    1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |      1 |         |         |       6 (100)|         |        4 |00:00:00.01 |       9 |         |         |        |
|   1 |  HASH GROUP BY                |       |      1 |       5 |     155 |       6  (17)| 00:00:01 |        4 |00:00:00.01 |       9 |    1116K|    1116K|    772K (0)|
|*  2 |   HASH JOIN                |       |      1 |       9 |     279 |       5   (0)| 00:00:01 |        9 |00:00:00.01 |       9 |    2078K|    2078K|    784K (0)|
|   3 |    VIEW                 | VW_GBF_1 |      1 |       2 |      32 |       2   (0)| 00:00:01 |        2 |00:00:00.01 |       2 |         |         |        |
|   4 |     HASH GROUP BY            |       |      1 |       2 |      26 |       2   (0)| 00:00:01 |        2 |00:00:00.01 |       2 |    1558K|    1558K|    618K (0)|
|*  5 |      TABLE ACCESS BY INDEX ROWID BATCHED| DEPT       |      1 |       2 |      26 |       2   (0)| 00:00:01 |        2 |00:00:00.01 |       2 |         |         |        |
|   6 |       INDEX FULL SCAN            | PK_DEPT  |      1 |       4 |         |       1   (0)| 00:00:01 |        4 |00:00:00.01 |       1 |         |         |        |
|   7 |    TABLE ACCESS FULL            | EMP       |      1 |      14 |     210 |       3   (0)| 00:00:01 |       14 |00:00:00.01 |       6 |         |         |        |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$926C320A
   3 - SEL$F8F0E2FD / VW_GBF_1@SEL$6DFE8F9D
   4 - SEL$F8F0E2FD
   5 - SEL$F8F0E2FD / D@MAIN
   6 - SEL$F8F0E2FD / D@MAIN
   7 - SEL$926C320A / E@MAIN
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$F8F0E2FD")
      OUTLINE_LEAF(@"SEL$926C320A")
      PLACE_GROUP_BY(@"MAIN" ( "D"@"MAIN" ) 1)
      OUTLINE(@"SEL$6DFE8F9D")
      OUTLINE(@"MAIN")
      NO_ACCESS(@"SEL$926C320A" "VW_GBF_1"@"SEL$6DFE8F9D")
      FULL(@"SEL$926C320A" "E"@"MAIN")
      LEADING(@"SEL$926C320A" "VW_GBF_1"@"SEL$6DFE8F9D" "E"@"MAIN")
      USE_HASH(@"SEL$926C320A" "E"@"MAIN")
      USE_HASH_AGGREGATION(@"SEL$926C320A")
      INDEX(@"SEL$F8F0E2FD" "D"@"MAIN" ("DEPT"."DEPTNO"))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$F8F0E2FD" "D"@"MAIN")
      USE_HASH_AGGREGATION(@"SEL$F8F0E2FD")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("E"."DEPTNO"="ITEM_1")
   5 - filter(("D"."DNAME"='ACCOUNTING' OR "D"."DNAME"='SALES'))
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (rowset=256"JOB"[VARCHAR2,9], SUM("SAL"*"ITEM_2")[22]
   2 - (#keys=1; rowset=256"ITEM_2"[NUMBER,22], "JOB"[VARCHAR2,9], "SAL"[NUMBER,22]
   3 - (rowset=256"ITEM_1"[NUMBER,22], "ITEM_2"[NUMBER,22]
   4 - (rowset=256"D"."DEPTNO"[NUMBER,22], COUNT(*)[22]
   5 - "D"."DEPTNO"[NUMBER,22]
   6 - "D".ROWID[ROWID,10], "D"."DEPTNO"[NUMBER,22]
   7 - (rowset=256"JOB"[VARCHAR2,9], "SAL"[NUMBER,22], "E"."DEPTNO"[NUMBER,22]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2
---------------------------------------------------------------------------
 
   1 -    SEL$926C320A
       -  place_group_by(@main(d@main))
       -  qb_name(main)
 
Query Block Registry:
---------------------
 
  <q o="3"><n><![CDATA[MAIN]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[MAIN]]></s></h><h><t><![CDATA[E]]></t><s><![CDATA[MAIN]]></s></h></f></q>
  <q o="43" f="y" h="y"><n><![CDATA[SEL$926C320A]]></n><p><![CDATA[MAIN]]></p><i><o><t>TA</t><v><![CDATA[D@MAIN]]></v></o></i><f><h><t><![CDATA[E]]></t><s><![CDATA
    [MAIN]]></s></h><h><t><![CDATA[VW_GBF_1]]></t><s><![CDATA[SEL$6DFE8F9D]]></s></h></f></q>
  <q o="15" f="y"><n><![CDATA[SEL$F8F0E2FD]]></n><p><![CDATA[SEL$6DFE8F9D]]></p><f><h><t><![CDATA[D]]></t><s><![CDATA[MAIN]]></s></h></f></q>
  <q o="26"><n><![CDATA[SEL$6DFE8F9D]]></n><p><![CDATA[MAIN]]></p><i><o><t>FA</t><v><![CDATA["D"@"MAIN"]]></v></o></i><f><h><t><![CDATA[E]]></t><s><![CDATA[MAIN]]>
    </s></h><h><t><![CDATA[VW_GBF_1]]></t><s><![CDATA[SEL$6DFE8F9D]]></s></h></f></q>
 
 
 
93 rows selected.

27번째 줄 : GBP가 발생하여 VW_GBF_1 뷰가 생성됨, 이로 인해 group by 가 총 2번 수행됨

 

 

GBP 미발생시

(GBP를 발생시키지 않기 위해  no_place_group_by 힌트를 사용함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
SQL>
select /*+ gather_plan_statistics qb_name(main) no_place_group_by(@main(d@main)) */
e.job, sum(sal)
from emp e, dept d
where e.deptno = d.deptno
and d.dname in('ACCOUNTING''SALES')
group by job;
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULLNULL'ADVANCED ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    2rq3vgb7sdshm, child number 0
-------------------------------------
select /*+ gather_plan_statistics qb_name(main)
no_place_group_by(@main(d@main)) */ e.job, sum(sal) from emp e, dept d
where e.deptno = d.deptno and d.dname in('ACCOUNTING''SALES') group
by job
 
Plan hash value: 2006461124
 
-------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation        | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |    OMem |    1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |       |      1 |         |         |       6 (100)|         |        4 |00:00:00.01 |       9 |         |         |        |
|   1 |  HASH GROUP BY        |       |      1 |       5 |     140 |       6  (17)| 00:00:01 |        4 |00:00:00.01 |       9 |    1116K|    1116K|    772K (0)|
|*  2 |   HASH JOIN        |       |      1 |       9 |     252 |       5   (0)| 00:00:01 |        9 |00:00:00.01 |       9 |    2546K|    2546K|    866K (0)|
|*  3 |    TABLE ACCESS FULL| DEPT |      1 |       2 |      26 |       2   (0)| 00:00:01 |        2 |00:00:00.01 |       2 |         |         |        |
|   4 |    TABLE ACCESS FULL| EMP  |      1 |      14 |     210 |       3   (0)| 00:00:01 |       14 |00:00:00.01 |       6 |         |         |        |
-------------------------------------------------------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - MAIN
   3 - MAIN / D@MAIN
   4 - MAIN / E@MAIN
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"MAIN")
      FULL(@"MAIN" "D"@"MAIN")
      FULL(@"MAIN" "E"@"MAIN")
      LEADING(@"MAIN" "D"@"MAIN" "E"@"MAIN")
      USE_HASH(@"MAIN" "E"@"MAIN")
      USE_HASH_AGGREGATION(@"MAIN")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("E"."DEPTNO"="D"."DEPTNO")
   3 - filter(("D"."DNAME"='ACCOUNTING' OR "D"."DNAME"='SALES'))
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (rowset=256"JOB"[VARCHAR2,9], SUM("SAL")[22]
   2 - (#keys=1; rowset=256"JOB"[VARCHAR2,9], "SAL"[NUMBER,22]
   3 - (rowset=256"D"."DEPTNO"[NUMBER,22]
   4 - (rowset=256"JOB"[VARCHAR2,9], "SAL"[NUMBER,22], "E"."DEPTNO"[NUMBER,22]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2
---------------------------------------------------------------------------
 
   1 -    MAIN
       -  no_place_group_by(@main(d@main))
       -  qb_name(main)
 
Query Block Registry:
---------------------
 
  <q o="3" f="y"><n><![CDATA[MAIN]]></n><f><h><t><![CDATA[D]]></t><s><![CDATA[MAIN]]></s></h><h><t><![CDATA[E]]></t><s><![CDATA[MAIN]]></s>
    </h></f></q>
 
 
 
73 rows selected.

25번째 줄 : GBP가 발생하지 않아서 하여 group by 가 한번만 수행됨

 

 

COMPARE_PLANS 비교

reference_plan 에 기존 플랜, compare_plan_list 에 비교 대상 플랜을 넣어 비교함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
SQL> 
VARIABLE v_rep CLOB
BEGIN
:v_rep := DBMS_XPLAN.COMPARE_PLANS(
reference_plan => cursor_cache_object('db26h2x1zp29w'null),
compare_plan_list =>
plan_object_list(cursor_cache_object('2rq3vgb7sdshm'null)),
type => 'TEXT',
level => 'TYPICAL',
section => 'ALL');
END;
/
 
PL/SQL procedure successfully completed.
 
SQL> 
set pages 50000
set long 100000
set lines 210
col report for a200
SELECT :v_rep REPORT FROM DUAL;
REPORT
---------------------------------------------------------------------------------------------
 
COMPARE PLANS REPORT
---------------------------------------------------------------------------------------------
  Current user         : IMSI
  Total number of plans  : 2
  Number of findings     : 1
---------------------------------------------------------------------------------------------
 
COMPARISON DETAILS
---------------------------------------------------------------------------------------------
 Plan Number        : 1 (Reference Plan)
 Plan Found        : Yes
 Plan Source        : Cursor Cache
 SQL ID         : db26h2x1zp29w
 Child Number        : 0
 Plan Database Version    : 19.0.0.0
 Parsing Schema     : "IMSI"
 SQL Text        : select /*+ gather_plan_statistics qb_name(main)
            place_group_by(@main(d@main)) */ e.job, sum(sal) from
            emp e, dept d where e.deptno = d.deptno and d.dname
            in('ACCOUNTING''SALES') group by job
 
Plan
-----------------------------
 
 Plan Hash Value  : 2777097701
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                  | Name     | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |        |        |     6 |          |
|   1 |   HASH GROUP BY               |         |      5 |    155 |     6 | 00:00:01 |
| * 2 |    HASH JOIN                  |         |      9 |    279 |     5 | 00:00:01 |
|   3 |     VIEW                  | VW_GBF_1 |      2 |     32 |     2 | 00:00:01 |
|   4 |      HASH GROUP BY              |         |      2 |     26 |     2 | 00:00:01 |
| * 5 |       TABLE ACCESS BY INDEX ROWID BATCHED | DEPT     |      2 |     26 |     2 | 00:00:01 |
|   6 |        INDEX FULL SCAN              | PK_DEPT  |      4 |        |     1 | 00:00:01 |
|   7 |     TABLE ACCESS FULL              | EMP      |     14 |    210 |     3 | 00:00:01 |
-----------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("E"."DEPTNO"="ITEM_1")
* 5 - filter(("D"."DNAME"='ACCOUNTING' OR "D"."DNAME"='SALES'))
 
---------------------------------------------------------------------------------------------
 Plan Number        : 2
 Plan Found        : Yes
 Plan Source        : Cursor Cache
 SQL ID         : 2rq3vgb7sdshm
 Child Number        : 0
 Plan Database Version    : 19.0.0.0
 Parsing Schema     : "IMSI"
 SQL Text        : select /*+ gather_plan_statistics qb_name(main)
            no_place_group_by(@main(d@main)) */ e.job, sum(sal)
            from emp e, dept d where e.deptno = d.deptno and
            d.dname in('ACCOUNTING''SALES') group by job
 
Plan
-----------------------------
 
 Plan Hash Value  : 2006461124
 
-----------------------------------------------------------------------
| Id  | Operation          | Name | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |        |        |     6 |          |
|   1 |   HASH GROUP BY       |      |      5 |    140 |     6 | 00:00:01 |
| * 2 |    HASH JOIN          |      |      9 |    252 |     5 | 00:00:01 |
| * 3 |     TABLE ACCESS FULL | DEPT |      2 |     26 |     2 | 00:00:01 |
|   4 |     TABLE ACCESS FULL | EMP  |     14 |    210 |     3 | 00:00:01 |
-----------------------------------------------------------------------
 
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("E"."DEPTNO"="D"."DEPTNO")
* 3 - filter(("D"."DNAME"='ACCOUNTING' OR "D"."DNAME"='SALES'))
 
 
Comparison Results (1):
-----------------------------
 1. Query block MAIN: Transformation PLACE GROUP BY occurred only in the
    reference plan (result query block: SEL$926C320A).
 
 
---------------------------------------------------------------------------------------------

103번째 줄 : Comparison Results에 reference plan(기존플랜)에서만 PLACE GROUP BY Transformation 이 발생했다고 나옴

 

 

위와 같이 DBMS_XPLAN.COMPARE_PLANS를 이용해 실행계획을 oracle 내에서 비교해볼수있음

 

 

참조 : 

2736319.1, 2735151.1
https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/generating-and-displaying-execution-plans.html#GUID-0023D232-5695-4BA8-89C5-88672B7647E2
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_XPLAN.html#GUID-5A80E12C-F126-413D-8FF7-FC04B382FA92
https://hrjeong.tistory.com/175
https://smarttechways.com/2019/08/08/compare-execution-plan-of-two-sql-statements-in-oracle-19c/
The Logical Optimizer 서적(SJ, OE, GBP 부분)