Understanding Oracle QUERY PLAN
When you fire an SQL query to Oracle, Oracle first comes up with a query execution plan in order to fetch the desired data from the physical tables. This query execution plan is crucial as different execution plan take different time for the query to execute.
Oracle Query Execution Plan actually depends on the choice of Oracle optimizer – Rule based (RBO) Or Cost based (CBO) Optimizer. For Oracle 10g, CBO is the default optimizer. Cost Based optimizer enforces Oracle to generate the optimization plan by taking all the related table statistics into consideration. On the other hand, RBO uses a fixed set of pre-defined rules to generate the query plan. Obviously such fixed set of rules might not always be accurate to come up with most efficient plan, as actual plan depends a lot on the nature and volume of tables’ data.
But this article is not for comparing RBO and CBO (In fact, there is not much point in comparing these two). This article will briefly help you understand,
So let’s begin. I will be using Oracle 10g server and SQL *Plus client to demonstrate all the details.
Let’s start by creating a simple product table with the following structure,
ID number(10) NAME varchar2(100) DESCRIPTION varchar2(255) SERVICE varchar2(30) PART_NUM varchar2(50) LOAD_DATE date
Next I will insert 15,000 records into this newly created table (data taken from one of my existing product table from one of my client’s production environment).
Remember, currently there is no index on the table.
So we start our journey by writing a simple select statement on this table as below,
SQL> explain plan for select * from product; Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ---------------------------------------------------------- Plan hash value: 3917577207 ------------------------------------- | Id | Operation | Name | ------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS FULL | PRODUCT| ------------------------------------- Note ----- - rule based optimizer used (consider using cbo)
Notice that optimizer has decided to use RBO instead of CBO as Oracle does not have any statistics for this table. Let’s now build some statistics for this table by issuing the following command,
SQL> Analyze table product compute statistics;
Now let’s do the same experiment once again,
SQL> explain plan for select * from product; Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ----------------------------------------------------- Plan hash value: 3917577207 ----------------------------------------------------- | Id | Operation | Name | Rows | Bytes | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 15856 | 1254K| | 1 | TABLE ACCESS FULL | PRODUCT | 15856 | 1254K| -----------------------------------------------------
You can easily see that this time optimizer has used Cost Based Optimizer (CBO) and has also detailed some additional information (e.g. Rows etc.)
The point to note here is, Oracle is reading the whole table (denoted by TABLE ACCESS FULL) which is very obvious because the select * statement that is being fired is trying to read everything. So, there’s nothing interesting up to this point.
Now let’s add a WHERE clause in the query and also create some additional indexes on the table.
SQL> create unique index idx_prod_id on product (id) compute statistics; Index created. SQL> explain plan for select id from product where id = 100; Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT --------------------------------------------------------- Plan hash value: 2424962071 --------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | --------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 4 | |* 1 | INDEX UNIQUE SCAN | IDX_PROD_ID | 1 | 4 | ---------------------------------------------------------
So the above statement indicates that CBO is performing Index Unique Scan. This means, in order to fetch the id value as requested, Oracle is actually reading the index only and not the whole table. Of course this will be faster than FULL TABLE ACCESS operation shown earlier.
Searching the index is a fast and an efficient operation for Oracle and when Oracle finds the desired value it is looking for (in this case id=100), it can also find out the rowid of the record in product table that has id=100. Oracle can then use this rowid to fetch further information if requested in query. See below,
SQL> explain plan for select * from product where id = 100; Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ---------------------------------------------------------- Plan hash value: 3995597785 ---------------------------------------------------------- | Id | Operation | Name |Rows | Bytes| ---------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 81 | | 1 | TABLE ACCESS BY INDEX ROWID| PRODUCT| 1 | 81 | |* 2 | INDEX UNIQUE SCAN | IDX_PROD_ID | 1 | | ----------------------------------------------------------
TABLE ACCESS BY INDEX ROWID is the interesting part to check here. Since now we have specified select * for id=100, so Oracle first use the index to obtain the rowid of the record. And then it selects all the columns by the rowid.
But what if we specify a >, or between criteria in the WERE clause instead of equality condition? Like below,
SQL> explain plan for select id from product where id <10 Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT --------------------------------------------- Plan hash value: 1288034875 ------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | ------------------------------------------------------- | 0 | SELECT STATEMENT | | 7 | 28 | |* 1 | INDEX RANGE SCAN| IDX_PROD_ID | 7 | 28 | -------------------------------------------------------
So this time CBO goes for an Index Range Scan instead of INDEX UNIQUE SCAN. The same thing will normally happen if we use a between clause also.
Now, let’s see another interesting aspect of INDEX scan here by just altering the “ 10”. Before we see the outcome, just remind yourself that there are 15000 over products with their ids starting from 1 to 15000+. So if we write “10” we are likely to get almost 14990+ records in return. So does Oracle go for an INDEX RANGE SCAN in this case? Let’s see,
SQL> explain plan for select id from product where id>10; Explained. SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ------------------------------------------------ Plan hash value: 2179322443 -------------------------------------------------------- | Id | Operation | Name | Rows |Bytes | -------------------------------------------------------- | 0 | SELECT STATEMENT | | 15849|63396 | |* 1 | INDEX FAST FULL SCAN| IDX_PROD_ID| 15849|63396 | ---------------------------------------------------------
So, Oracle is actually using a INDEX FAST FULL SCAN to “quickly” scan through the index and return the records from table.
FTS
Index Unique Scan
Index Fast Full Scan
Index Full Scan
So I think we covered the basics of simple SELECT queries running on a single table. We will move forward to understand how the query plan changes when we join more than one table. This I will cover up in the next article. Happy reading!