Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005

rekharaghuram
from rekharaghuram More from this publisher
05.11.2015 Views

CHAPTER 11 ■ INDEXES 479 Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(TO_NUMBER("X")=5) it uses the index, but not for a UNIQUE SCAN as we might expect—it is FULL SCANNING this index. The reason lies in the last line of output there: filter(TO_NUMBER("X")=5). There is an implicit function being applied to the database column. The character string stored in X must be converted to a number prior to comparing to the value 5. We cannot convert 5 to a string, since our NLS settings control what 5 might look like in a string (it is not deterministic), so we convert the string into a number, and that precludes the use of the index to rapidly find this row. If we simply compare strings to strings ops$tkyte@ORA10GR1> delete from plan_table; 2 rows deleted. ops$tkyte@ORA10GR1> explain plan for select * from t where x = '5'; Explained. ops$tkyte@ORA10GR1> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ------------------------------------------------------------------- Plan hash value: 1301177541 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 12 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 12 | 1 (0)| 00:00:01 | |* 2 | INDEX UNIQUE SCAN | T_PK | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("X"='5') we get the expected INDEX UNIQUE SCAN, and we can see the function is not being applied. You should always avoid implicit conversions anyway. Always compare apples to apples and oranges to oranges. Another case where this comes up frequently is with dates. We try to query: -- find all records for today select * from t where trunc(date_col) = trunc(sysdate); and discover that the index on DATE_COL will not be used. We can either index the TRUNC(DATE_COL) or, perhaps more easily, query using range comparison operators. The following demonstrates the use of greater than and less than on a date. Once we realize that the condition TRUNC(DATE_COL) = TRUNC(SYSDATE)

480 CHAPTER 11 ■ INDEXES is the same as the condition select * from t where date_col >= trunc(sysdate) and date_col < trunc(sysdate+1) this moves all of the functions to the right-hand side of the equation, allowing us to use the index on DATE_COL (and it has the same exact effect as WHERE TRUNC(DATE_COL) = ➥ TRUNC(SYSDATE)). If possible, you should always remove the functions from database columns when they are in the predicate. Not only will doing so allow for more indexes to be considered for use, but also it will reduce the amount of processing the database needs to do. In the preceding case, when we used where date_col >= trunc(sysdate) and date_col < trunc(sysdate+1) the TRUNC values are computed once for the query, and then an index could be used to find just the qualifying values. When we used TRUNC(DATE_COL) = TRUNC(SYSDATE), the TRUNC(DATE_COL) had to be evaluated once per row for every row in the entire table (no indexes). Case 5 The index, if used, would actually be slower. I see this a lot—people assume that, of course, an index will always make a query go faster. So, they set up a small table, analyze it, and find that the optimizer doesn’t use the index. The optimizer is doing exactly the right thing in this case. Oracle (under the CBO) will use an index only when it makes sense to do so. Consider this example: ops$tkyte@ORA10GR1> create table t 2 ( x, y , primary key (x) ) 3 as 4 select rownum x, object_name 5 from all_objects 6 / Table created. ops$tkyte@ORA10GR1> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', cascade=>true ); 4 end; 5 / PL/SQL procedure successfully completed. If we run a query that needs a relatively small percentage of the table, as follows:

CHAPTER 11 ■ INDEXES 479<br />

Predicate Information (identified by operation id):<br />

---------------------------------------------------<br />

2 - filter(TO_NUMBER("X")=5)<br />

it uses the index, but not for a UNIQUE SCAN as we might expect—it is FULL SCANNING this index.<br />

The reason lies in the last line of output there: filter(TO_NUMBER("X")=5). There is an implicit<br />

function being applied to the database column. The character string stored in X must be converted<br />

to a number prior to comparing to the value 5. We cannot convert 5 to a string, since<br />

our NLS settings control what 5 might look like in a string (it is not deterministic), so we convert<br />

the string into a number, <strong>and</strong> that precludes the use of the index to rapidly find this row.<br />

If we simply compare strings to strings<br />

ops$tkyte@ORA10GR1> delete from plan_table;<br />

2 rows deleted.<br />

ops$tkyte@ORA10GR1> explain plan for select * from t where x = '5';<br />

Explained.<br />

ops$tkyte@ORA10GR1> select * from table(dbms_xplan.display);<br />

PLAN_TABLE_OUTPUT<br />

-------------------------------------------------------------------<br />

Plan hash value: 1301177541<br />

------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 1 | 12 | 1 (0)| 00:00:01 |<br />

| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 12 | 1 (0)| 00:00:01 |<br />

|* 2 | INDEX UNIQUE SCAN | T_PK | 1 | | 1 (0)| 00:00:01 |<br />

------------------------------------------------------------------------------------<br />

Predicate Information (identified by operation id):<br />

---------------------------------------------------<br />

2 - access("X"='5')<br />

we get the expected INDEX UNIQUE SCAN, <strong>and</strong> we can see the function is not being applied.<br />

You should always avoid implicit conversions anyway. Always compare apples to apples <strong>and</strong><br />

oranges to oranges. Another case where this comes up frequently is with dates. We try to<br />

query:<br />

-- find all records for today<br />

select * from t where trunc(date_col) = trunc(sysdate);<br />

<strong>and</strong> discover that the index on DATE_COL will not be used. We can either index the<br />

TRUNC(DATE_COL) or, perhaps more easily, query using range comparison operators. The<br />

following demonstrates the use of greater than <strong>and</strong> less than on a date. Once we realize<br />

that the condition<br />

TRUNC(DATE_COL) = TRUNC(SYSDATE)

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!