1) Sub-optimal index.
SQL> -- Example 1: sub-optimal index
SQL>
SQL> select /* KM1 */ job_id, department_id, last_name
2 from employees
3 where job_id = 'SA_REP'
4 and department_id is null ;
JOB_ID DEPARTMENT_ID LAST_NAME
---------- --------------- -------------------------
SA_REP . Grant
SQL>
SQL> @pln KM1
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------
SQL_ID cdqaq2k8dvvma, child number 0
-------------------------------------
select /* KM1 */ job_id, department_id,
last_name from employees where job_id = 'SA_REP' and department_id is
null
Plan hash value: 1019430118save
| Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 4 |
|* 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 1 | 1 | 1 | 4 |
|* 2 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 30 | 30 | 2 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPARTMENT_ID" IS NULL)
2 - access("JOB_ID"='SA_REP')
===============
Step *2) Using INDEX RANG SCAN to search the table to get the ROWID, predicate is 'JOB_ID = SA_REP'. 30 rows returned.
*1) Continue process the 30 rows, accessing these 30 rows by ROWID and filter out the 'DEPARTMENT_ID IS NULL' rows. 1 row returned.
This example shows that the index range scan returns too many rows which will be dropped later which is a tremendous waste if the configuration is large.
We can create index for deparment_id and job_id to solve this problem.
SQL> create index emp_job_dept_ix on employees (department_id, job_id) compute statistics ;
SQL> select /* KM2 */ job_id, department_id, last_name
2 from employees
3 where job_id = 'SA_REP'
4 and department_id is null ;
JOB_ID DEPARTMENT_ID LAST_NAME
---------- --------------- -------------------------
SA_REP . Grant
SQL>
SQL> @pln KM2
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
SQL_ID b4wnf48g9pgzy, child number 0
-------------------------------------
select /* KM2 */ job_id, department_id, last_name from employees where
job_id = 'SA_REP' and department_id is null
Plan hash value: 798439539
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 1 | 1 | 1 | 2 |
|* 2 | INDEX RANGE SCAN | EMP_JOB_DEPT_IX | 1 | 1 | 1 | 1 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID" IS NULL AND "JOB_ID"='SA_REP')
filter("JOB_ID"='SA_REP')
SQL>
===============
2) Missing Index
SQL> select /* KM3 */ last_name, phone_number
2 from employees
3 where phone_number = '650.507.9822';
LAST_NAME PHONE_NUMBER
------------------------- --------------------
Feeney 650.507.9822
SQL>
SQL> @pln KM3
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------
SQL_ID 8vzwg0vkrjp8r, child number 0
-------------------------------------
select /* KM3 */ last_name, phone_number from employees where
phone_number = '650.507.9822'
Plan hash value: 1445457117
----------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 7 |
|* 1 | TABLE ACCESS FULL | EMPLOYEES | 1 | 1 | 1 | 7 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("PHONE_NUMBER"='650.507.9822')
=====================================
Step *1) FULL TABLE ACCESS just return 1 row, which means the read/access to the other rows is totally wasted/dropped which means too much meaningful access/read to the rows.
Create an index for this column to solve this problem.
SQL> -- Create new index on phone_number
SQL>
SQL> create index emp_phone_ix on employees (phone_number) compute statistics ;
SQL>
SQL> select /* KM4 */ last_name, phone_number
2 from employees
3 where phone_number = '650.507.9822';
LAST_NAME PHONE_NUMBER
------------------------- --------------------
Feeney 650.507.9822
SQL>
SQL> @pln KM4
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
SQL_ID 3tcqa5jqsyzm0, child number 0
-------------------------------------
select /* KM4 */ last_name, phone_number from employees where
phone_number = '650.507.9822'
Plan hash value: 1086981517
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 3 |
| 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 1 | 1 | 1 | 3 |
|* 2 | INDEX RANGE SCAN | EMP_PHONE_IX | 1 | 1 | 1 | 2 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("PHONE_NUMBER"='650.507.9822')