EXPLAIN PLAN은 주어진 SQL 쿼리를 지금 실행한다면 이 쿼리의 쿼리 계획이 어떻게 될지를 오라클에게 알려달라고 하는 데 이용될 수 있는 SQL 명령이다.
SQL > @C:\oracle\product\10.2.0\db_1\RDBMS\ADMIN\utlxplan
테이블이 생성되었습니다.
경 과: 00:00:00.75
SQL > desc plan_table
이름 널? 유형
----------------------------------------------------------------------------------- -------- ------
STATEMENT_ID VARCHAR2(30)
PLAN_ID NUMBER
TIMESTAMP DATE
REMARKS VARCHAR2(4000)
OPERATION VARCHAR2(30)
~~~~~~~ (이하 생략)
-- CREATE 문법
CREATE GLOBAL TEMPORARY TABLE <table_name> (
<column_name> <column_data_type>,
<column_name> <column_data_type>,
<column_name> <column_data_type>)
\[ON COMMIT DELETE ROWS | ON COMMIT PRESERVE ROWS\];
on commit delete rows | 트랜젝션을 발생시킨 후, commit문을 실행할 때 없어지는 방법 한 transaction 동안만 data를 저장(default임) |
on commit preserve rows | 트랜젝션을 종료하면(commit) 테이블 내에 데이터가 저장되었다가 세션을 종료하면 임시 테이블에 저장되었던 데이터들이 모두 없어지는 방법 한 session 동안만 data를 저장 |
CREATE GLOBAL TEMPORARY TABLE PLAN_TABLE (
statement_id varchar2(30),
plan_id number,
timestamp date,
remarks varchar2(4000),
operation varchar2(30),
options varchar2(255),
object_node varchar2(128),
object_owner varchar2(30),
object_name varchar2(30),
object_alias varchar2(65),
object_instance numeric,
object_type varchar2(30),
optimizer varchar2(255),
search_columns number,
id numeric,
parent_id numeric,
depth numeric,
position numeric,
cost numeric,
cardinality numeric,
bytes numeric,
other_tag varchar2(255),
partition_start varchar2(255),
partition_stop varchar2(255),
partition_id numeric,
other long,
distribution varchar2(30),
cpu_cost numeric,
io_cost numeric,
temp_space numeric,
access_predicates varchar2(4000),
filter_predicates varchar2(4000),
projection varchar2(4000),
time numeric,
qblock_name varchar2(30),
other_xml clob
)
ON COMMIT PRESERVE ROWS;
-- 모든권한
grant ALL on PLAN_TABLE 모든유저;
-- 동의어 생성 : PUBLIC SYNONYM 을 생성할수 있는 권한이 있는 유저로 가야한다.
CREATE PUBLIC SYNONYM PLAN_TABLE FOR PLAN_TABLE;
select plan_table_output
from table(dbms_xplan.display('plan_table',null,'serial'));
select plan_table_output
from table(dbms_xplan.display('plan_table',null,'serial'));
FUNCTION DISPLAY(TABLE_NAME VARCHAR2 DEFAULT 'PLAN_TABLE',
STATEMENT_ID VARCHAR2 DEFAULT NULL,
FORMAT VARCHAR2 DEFAULT 'TYPICAL',
FILTER_PREDS VARCHAR2 DEFAULT NULL)
파라미터 | 설명 |
---|---|
TABLE_NAME | Execution Plan이 저장되는 테이블을 테이블을 지정하며, Defalut는 'PLAN_TABLE'이다. |
STATEMENT_ID | Execution Plan시 SET STATEMENT_ID를 지정한 경우 이를 불러올 수 있다. 값이 NULL일 경우 마지막에 실행된 문장을 불러온다. |
FORMAT(BASIC) | 가장 기본적인 정보만 보여줌 |
FORMAT(TYPICAL) | Format의 Default값인 Typical은 SQL 튜닝에 필요한 Normal한 정보를 보여줌 SQL 튜닝에 가장 유용하게 사용되는 Predicate Information이 제공된다 |
FORMAT(ALL) | Typical Format에 Query Block Name과 Column Projection Information이 추가로 제공된다 |
FORMAT(OUTLINE) | Typical Format에 추가적으로 Hidden Hint인 Outline Global Hint를 제공한다 |
FORMAT(ADVANCED) | ALL Format에 OUTLINE Format를 합친 정보를 제공한다 |
FILTER_PREDS | 저장된 PLAN에서 일부 Row 또는 Row Set을 제한하여 출력할 수 있다. |
SQL > create table t1(c1 int, c2 int);
테이블이 생성되었습니다.
경 과: 00:00:00.28
SQL > create table t2(c1 int, c2 int);
테이블이 생성되었습니다.
경 과: 00:00:00.00
SQL > create index t1_n1 on t1(c1);
인덱스가 생성되었습니다.
경 과: 00:00:00.07
SQL > create index t2_n1 on t2(c1)
2 ;
인덱스가 생성되었습니다.
경 과: 00:00:00.01
SQL > insert into t1
2 select level,level from dual connect by level <= 1000;
1000 개의 행이 만들어졌습니다.
경 과: 00:00:00.06
SQL > insert into t2
2 select level,level from dual connect by level <= 1000
3 ;
1000 개의 행이 만들어졌습니다.
경 과: 00:00:00.01
SQL > EXEC DBMS_STATS.GATHER_TABLE_STATS('HNJ','T1', METHOD_OPT =>'FOR ALL COLUMNS SIZE 1', NO_INVALIDATE => FALSE);
PL/SQL 처리가 정상적으로 완료되었습니다.
경 과: 00:00:01.03
SQL > EXEC DBMS_STATS.GATHER_TABLE_STATS('HNJ','T2', METHOD_OPT =>'FOR ALL COLUMNS SIZE 1', NO_INVALIDATE => FALSE);
PL/SQL 처리가 정상적으로 완료되었습니다.
경 과: 00:00:00.06
SQL >explain plan for
2 select /*+ use_nl(t1 t2) */ *
3 from t1, t2
4 where t1.c1 = t2.c2
5 and t1.c1 = 1;
해석되었습니다.
경 과: 00:00:00.12
SQL > select * from table(dbms_xplan.display('plan_table',null,'typical',null));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------
Plan hash value: 1755794669
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 14 | 5 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 14 | 5 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 7 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | T2 | 1 | 7 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T1"."C1"=1)
4 - filter("T2"."C2"=1)
17 개의 행이 선택되었습니다.
경 과: 00:00:00.09
SQL > explain plan for
2 select /*+ use_hash(t1 t2) */ *
3 from t1, t2
4 where t1.c1 = t2.c2
5 and t1.c2 = 1;
해석되었습니다.
경 과: 00:00:00.00
SQL > select * from table(dbms_xplan.display('plan_table',null,'typical',null));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 14 | 7 (15)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 14 | 7 (15)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| T1 | 1 | 7 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 1000 | 7000 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."C1"="T2"."C2")
2 - filter("T1"."C2"=1)
16 개의 행이 선택되었습니다.
경 과: 00:00:00.09
SQL >
구분 | 방식 | 결론 |
---|---|---|
Nested Loops Join | 선행 Table읽으면서 후행 Table을 한번씩 Access | 실제 Join은 후행 Table에 대한 Access에서 발생하기 때문에 실제 Join은 후행 Table에 대한 Acccess에서 발생한다. 그러므로 후행 Table을 읽는 단계가 Access Predicate가 된다. |
Hash Join | 선행 Table을 먼저 Build한 후, 후행 Table과 한번에 Join | 실제 Join이 발생하는 Hash Join 단계가 Access Predicat로 표현된다. |
FUNCTION DISPLAY_CURSOR(SQL_ID VARCHAR2 DEFAULT NULL,
CURSOR_CHILD_NO INTEGER DEFAULT 0,
FORMAT VARCHAR2 DEFAULT 'TYPICAL'))
파라미터 | 설명 |
---|---|
TABLE_NAME | 실제 수행된 SQL의 SQL_ID 값을 입력하면 해당 SQL의 실행계획을 볼 수 있다. SQL_ID 값은 V$SQL, V$SQLAREA에서 확인할 수 있다. SQL_ID 값을 명시하지 않으면 해당 세션의 마지막에 실행된 문장을 의미한다. |
STATEMENT_ID | 해당 SQL_ID의 CHILD NUMBER 값을 지정한다. CURSOR_CHILD_NO 값을 명시하지 않으면 해당 SQL_ID의 첫번째 CURSOR_CHILD_NO 값을 가져온다. |
FORMAT | 저장된 PLAN을 어떤 Format으로 보여줄 지 결정하는 파라미터 |
오라클은 라이브러리 캐시에 캐시에 캐싱돼 있는 각 커서에 대한 수행통계를 볼 수 있도록 v$sql 뷰를 제공한다. 이것과 함께 _id 값으로 조인해서 사용할 수 있도록 제공되는 뷰가 몇 가지 더 있는데, 그 중 활용도가 가장 높은 것이 v$sql_plan과 v$sql_plan_statistics다. 그리고 이 두 뷰를 합쳐서 보여주는 것이 V$SQL_PLAN_STATISTICS_ALL 이다.
구분 | FORMAT | 내용 |
---|---|---|
예측내용 | BASIC | DBMS_XPLAN.DISPLAY 포멧과 같은 내용 |
예측내용 | TYPICAL | DBMS_XPLAN.DISPLAY 포멧과 같은 내용 |
예측내용 | ALL | DBMS_XPLAN.DISPLAY 포멧과 같은 내용 |
예측내용 | OUTLINE | DBMS_XPLAN.DISPLAY 포멧과 같은 내용 |
예측내용 | ADVANCED | DBMS_XPLAN.DISPLAY 포멧과 같은 내용 |
실측내용 | ALLSTATS | 실제 액세스한 로우수와 수행시간 그리고 CR, PR, PW 정보를 보여준다. 수행횟수에 따라 누적된 값을 보여준다 |
실측내용 | ALLSTATS LAST | 실제 액세스한 로우수와 수행시간 그리고 CR, PR, PW 정보를 보여준다. 가장 최근에 수행된 값만 보여준다. |
실측내용 | ADVANCED ALLSTATS LAST | DBMS_XPLAN.DISPLAY_CURSOR에서 지원하는 모든 Format의 정보를 보여준다. |
SQL > set serveroutput off
SQL > select *
2 from scott.emp e, scott.dept d
3 where d.deptno = e.deptno
4 and e.sal >= 1000;
~~~~~~~~~~~~~
24 개의 행이 선택되었습니다.
SQL > SELECT PREV_SQL_ID, PREV_CHILD_NUMBER
2 FROM V$SESSION
3 WHERE SID = USERENV('sid')
4 AND USERNAME IS NOT NULL
5 AND PREV_HASH_VALUE <> 0;
PREV_SQL_ID PREV_CHILD_NUMBER
------------- -----------------
2ja7z9r1v93a2 0
1 개의 행이 선택되었습니다.
SQL > SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('2ja7z9r1v93a2', 0, 'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID 2ja7z9r1v93a2, child number 0
-------------------------------------
select * from scott.emp e, scott.dept d where d.deptno = e.deptno
and e.sal >= 1000
Plan hash value: 615168685
-----------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------
|* 1 | HASH JOIN | | 24 | 825K| 825K| 659K (0)|
| 2 | TABLE ACCESS FULL| DEPT | 4 | | | |
|* 3 | TABLE ACCESS FULL| EMP | 24 | | | |
-----------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("D"."DEPTNO"="E"."DEPTNO")
3 - filter("E"."SAL">=1000)
Note
-----
- dynamic sampling used for this statement
- Warning: basic plan statistics not available. These are only collected when:
* hint 'gather_plan_statistics' is used for the statement or
* parameter 'statistics_level' is set to 'ALL', at session or system level
28 개의 행이 선택되었습니다.
경 과: 00:00:00.81
수행한SQL이 다음의 조건을 충족하지 않았기 때문에 기록이 안됨
SQL > ALTER SESSION SET "_ROWSOURCE_EXECUTION_STATISTICS" = TRUE;
세션이 변경되었습니다.
SQL > set serveroutput off
-- ALTER SESSION SET "_ROWSOURCE_EXECUTION_STATISTICS" = TRUE; 대신
-- /*+ GATHER_PLAN_STATISTICS */ 힌트를 사용해도 된다.
SQL > select *
2 from scott.emp e, scott.dept d
3 where d.deptno = e.deptno
4 and e.sal >= 1000;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO DEPTNO DNAME LOC
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ---------- ----
8499 ALLEN SALESMAN 7698 81/02/20 1600 300 30 30 SALES CHICAG
8521 WARD SALESMAN 7698 81/02/22 1250 500 30 30 SALES CHICAG
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 개의 행이 선택되었습니다.
경 과: 00:00:00.25
SQL > SELECT PREV_SQL_ID, PREV_CHILD_NUMBER
2 FROM V$SESSION
3 WHERE SID = USERENV('sid')
4 AND USERNAME IS NOT NULL
5 AND PREV_HASH_VALUE <> 0;
PREV_SQL_ID PREV_CHILD_NUMBER
------------- -----------------
aduuuwpa8f64v 0
1 개의 행이 선택되었습니다.
경 과: 00:00:00.11
SQL > SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('aduuuwpa8f64v', 0, 'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID aduuuwpa8f64v, child number 0
-------------------------------------
select * from scott.emp e, scott.dept d where d.deptno = e.deptno and e.sal >= 1000
Plan hash value: 615168685
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-
----------------------------------------------------------------------------------------------------
|* 1 | HASH JOIN | | 1 | 24 | 24 |00:00:00.01 | 16 | 825K| 825K| 679K (0)|
| 2 | TABLE ACCESS FULL| DEPT | 1 | 4 | 4 |00:00:00.01 | 7 | | | |
|* 3 | TABLE ACCESS FULL| EMP | 1 | 24 | 24 |00:00:00.01 | 9 | | | |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("D"."DEPTNO"="E"."DEPTNO")
3 - filter("E"."SAL">=1000)
Note
-----
- dynamic sampling used for this statement
24 개의 행이 선택되었습니다.
경 과: 00:00:00.45
SQL >
Note
: 부가적으로 필요한 정보를 제공한다. 위 예문에서 는 dynamic sampling 이 사용되었음 알려준다.
Oracle 10g에서는 통계 정보가 없는 Table 에 대해서 dynamic sampling 을 수행한다.
SQL > SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('7ww3qp7ahvbkk', 0, 'BASIC ROWS BYTES COST PREDICATE'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
EXPLAINED SQL STATEMENT:
------------------------
select * from scott.emp e, scott.dept d where d.deptno = e.deptno
and e.sal >= 1000
Plan hash value: 615168685
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 7 (100)|
|* 1 | HASH JOIN | | 28 | 1876 | 7 (15)|
| 2 | TABLE ACCESS FULL| DEPT | 4 | 120 | 3 (0)|
|* 3 | TABLE ACCESS FULL| EMP | 28 | 1036 | 3 (0)|
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("D"."DEPTNO"="E"."DEPTNO")
3 - filter("E"."SAL">=1000)
22 개의 행이 선택되었습니다.
경 과: 00:00:01.57
SQL >