CREATE TABLE table_explain_plan_1 ( id1 NUMBER(5,0) NOT NULL , id2 NUMBER(5,0) NOT NULL , type1 NUMBER(1,0) NOT NULL );
DECLARE indexLoop NUMBER(5,0); BEGIN FOR indexLoop IN 1..50000 LOOP IF MOD(indexLoop,2) = 0 THEN INSERT INTO table_explain_plan_1 (id1, id2, type1) VALUES (indexLoop, DBMS_RANDOM.VALUE(1,50000), 1); ELSE INSERT INTO table_explain_plan_1 (id1, id2, type1) VALUES (indexLoop, DBMS_RANDOM.VALUE(1,50000), 2); END IF; END LOOP; END; / COMMIT;
CREATE INDEX ix_table_explain_plan_1_type1 ON table_explain_plan_1 (type1); ANALYZE INDEX ix_table_explain_plan_1_type1 COMPUTE STATISTICS;The table contains 50,000 rows with an even distribution of type1 values (1 and 2), making it ideal for demonstrating execution plan behavior.
SELECT object_type, owner FROM dba_objects WHERE object_name = 'PLAN_TABLE';Query result:
| OBJECT_TYPE | OWNER | +--------------+--------+ | SYNONYM | PUBLIC |PLAN_TABLE is actually a public synonym that points to the underlying PLAN_TABLE$ table owned by SYS:
SELECT table_name, table_owner, owner FROM dba_synonyms WHERE synonym_name = 'PLAN_TABLE';Query result:
| table_name | table_owner | owner | +-------------+-------------+--------| | PLAN_TABLE$ | SYS | PUBLIC |The PLAN_TABLE$ is implemented as a Global Temporary Table (GTT) with session-specific storage, meaning each session maintains its own execution plan data that is automatically cleaned up when the session ends:
SELECT temporary, duration FROM dba_tables WHERE table_name = 'PLAN_TABLE$';Query result:
| temporary | duration | +-----------+-------------+ | Y | SYS$SESSION |The complete DDL for creating the PLAN_TABLE$ can be found in the "catplan.sql" script located in the "$ORACLE_HOME/rdbms/admin/" directory. This table structure has evolved across Oracle versions to support new optimizer features and execution plan attributes.
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, other_xml clob, 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) ) on commit preserve rows /
EXPLAIN PLAN FOR SELECT * FROM table_explain_plan_1 WHERE type1 = 1;After running EXPLAIN PLAN, you can retrieve the formatted execution plan using the DBMS_XPLAN.DISPLAY function, which provides a readable, hierarchical representation of the execution steps:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan hash value: 3358088968 ------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 24721 | 941K| 50 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TABLE_EXPLAIN_PLAN_1 | 24721 | 941K| 50 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IX_TABLE_EXPLAIN_PLAN_1_TYPE1 | 208 | | 49 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("TYPE1"=1)The execution plan shows that Oracle chose to use an index range scan followed by table access by rowid, which is typically efficient for selective queries. The asterisk (*) next to operation ID 2 indicates that predicate information is available for that step.
SET AUTOTRACE TRACEONLY EXPLAIN SELECT * FROM table_explain_plan_1 WHERE type1 = 1 /
Plan hash value: 3358088968 ------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 24721 | 941K| 50 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TABLE_EXPLAIN_PLAN_1 | 24721 | 941K| 50 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IX_TABLE_EXPLAIN_PLAN_1_TYPE1 | 208 | | 49 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("TYPE1"=1)Additional AUTOTRACE options include TRACEONLY STATISTICS (shows execution statistics without the plan) and ON (displays query results, execution plan, and statistics). The AUTOTRACE command automatically formats the output and doesn't require manual querying of PLAN_TABLE, making it convenient for interactive performance analysis sessions.
EXPLAIN PLAN SET STATEMENT_ID = 'table_explain_plan_1_1' FOR SELECT * FROM table_explain_plan_1 WHERE type1 = 1;The following query demonstrates how to construct a hierarchical view of the execution plan using Oracle's CONNECT BY syntax. This approach replicates the tree structure shown by DBMS_XPLAN.DISPLAY:
SELECT id , parent_id , cardinality AS "ROWS" , lpad(' ',level-1)||operation||' '||options||' '||object_name AS "STEP" FROM PLAN_TABLE CONNECT BY prior id = parent_id AND prior statement_id = statement_id START WITH id = 0 AND statement_id = 'table_explain_plan_1_1' ORDER BY id;Query result:
| id | parent_id | ROWS | STEP | +----+-----------+-------+---------------------------------------------------| | 0 | | 24721 | SELECT STATEMENT | | 1 | 0 | 24721 | TABLE ACCESS BY INDEX ROWID table_explain_plan_1 | | 2 | 1 | 208 | INDEX RANGE SCAN ix_table_explain_plan_1_type1 |This direct query approach allows you to access additional columns not displayed by standard formatting tools, such as CPU_COST, IO_COST, ACCESS_PREDICATES, and FILTER_PREDICATES, which can be valuable for detailed performance analysis and optimization efforts.