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 461 ops$tkyte@ORA10G> create index emp_soundex_idx on 2 emp( my_soundex(ename) ) tablespace ts4k; emp( my_soundex(ename) ) tablespace ts4k * ERROR at line 2: ORA-01450: maximum key length (3118) exceeded It is not that the index actually contains any keys that large, but that it could as far as the database is concerned. But the database understands SUBSTR. It sees the inputs to SUBSTR of 1 and 6, and knows the biggest return value from this is six characters; hence, it permits the index to be created. This size issue can get you, especially with concatenated indexes. Here is an example on an 8KB blocksize tablespace: ops$tkyte@ORA10G> create index emp_soundex_idx on 2 emp( my_soundex(ename), my_soundex(job) ); emp( my_soundex(ename), my_soundex(job) ) * ERROR at line 2: ORA-01450: maximum key length (6398) exceeded Here, the database thinks the maximum key size is 8,000 bytes and fails the CREATE once again. So, to index a user-written function that returns a string, we should constrain the return type in the CREATE INDEX statement. In the example, knowing that MY_SOUNDEX returns at most six characters, we are substringing the first six characters. We are now ready to test the performance of the table with the index on it. We would like to monitor the effect of the index on INSERTs as well as the speedup for SELECTs to see the effect on each. In the unindexed test case, our queries take over one second, and if we were to run SQL_TRACE and TKPROF during the inserts, we could observe that without the index, the insert of 9,999 records took about .5 seconds: insert into emp NO_INDEX (empno,ename,job,mgr,hiredate,sal,comm,deptno) select rownum empno, initcap(substr(object_name,1,10)) ename, substr(object_type,1,9) JOB, rownum MGR, created hiredate, rownum SAL, rownum COMM, (mod(rownum,4)+1)*10 DEPTNO from all_objects where rownum < 10000 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.03 0.06 0 0 0 0 Execute 1 0.46 0.43 0 15439 948 9999

462 CHAPTER 11 ■ INDEXES Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 2 0.49 0.50 0 15439 948 9999 But with the index, it takes about 1.2 seconds: call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.03 0.04 0 0 0 0 Execute 1 1.14 1.12 2 15650 7432 9999 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 2 1.17 1.16 2 15650 7432 9999 This was the overhead introduced in the management of the new index on the MY_SOUNDEX function—both in the performance overhead of simply having an index (any type of index will affect insert performance) and the fact that this index had to call a stored procedure 9,999 times. Now, to test the query, we’ll just rerun the query: ops$tkyte@ORA10G> REM reset our counter ops$tkyte@ORA10G> exec stats.cnt := 0 PL/SQL procedure successfully completed. ops$tkyte@ORA10G> set timing on ops$tkyte@ORA10G> set autotrace on explain ops$tkyte@ORA10G> select ename, hiredate 2 from emp 3 where substr(my_soundex(ename),1,6) = my_soundex('Kings') 4 / ENAME HIREDATE ---------- --------- Ku$_Chunk_ 10-AUG-04 Ku$_Chunk_ 10-AUG-04 Elapsed: 00:00:00.02 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=2 Card=1 Bytes=16) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (TABLE) (Cost=2 Card=1 Bytes=16) 2 1 INDEX (RANGE SCAN) OF 'EMP_SOUNDEX_IDX' (INDEX) (Cost=1 Card=35) ops$tkyte@ORA10G> set autotrace off ops$tkyte@ORA10G> set timing off ops$tkyte@ORA10G> set serveroutput on ops$tkyte@ORA10G> exec dbms_output.put_line( stats.cnt ); 2 PL/SQL procedure successfully completed.

CHAPTER 11 ■ INDEXES 461<br />

ops$tkyte@ORA10G> create index emp_soundex_idx on<br />

2 emp( my_soundex(ename) ) tablespace ts4k;<br />

emp( my_soundex(ename) ) tablespace ts4k<br />

*<br />

ERROR at line 2:<br />

ORA-01450: maximum key length (3118) exceeded<br />

It is not that the index actually contains any keys that large, but that it could as far as the<br />

database is concerned. But the database underst<strong>and</strong>s SUBSTR. It sees the inputs to SUBSTR of<br />

1 <strong>and</strong> 6, <strong>and</strong> knows the biggest return value from this is six characters; hence, it permits the<br />

index to be created. This size issue can get you, especially with concatenated indexes. Here is<br />

an example on an 8KB blocksize tablespace:<br />

ops$tkyte@ORA10G> create index emp_soundex_idx on<br />

2 emp( my_soundex(ename), my_soundex(job) );<br />

emp( my_soundex(ename), my_soundex(job) )<br />

*<br />

ERROR at line 2:<br />

ORA-01450: maximum key length (6398) exceeded<br />

Here, the database thinks the maximum key size is 8,000 bytes <strong>and</strong> fails the CREATE once<br />

again. So, to index a user-written function that returns a string, we should constrain the return<br />

type in the CREATE INDEX statement. In the example, knowing that MY_SOUNDEX returns at most<br />

six characters, we are substringing the first six characters.<br />

We are now ready to test the performance of the table with the index on it. We would like<br />

to monitor the effect of the index on INSERTs as well as the speedup for SELECTs to see the<br />

effect on each. In the unindexed test case, our queries take over one second, <strong>and</strong> if we were<br />

to run SQL_TRACE <strong>and</strong> TKPROF during the inserts, we could observe that without the index, the<br />

insert of 9,999 records took about .5 seconds:<br />

insert into emp NO_INDEX<br />

(empno,ename,job,mgr,hiredate,sal,comm,deptno)<br />

select rownum empno,<br />

initcap(substr(object_name,1,10)) ename,<br />

substr(object_type,1,9) JOB,<br />

rownum MGR,<br />

created hiredate,<br />

rownum SAL,<br />

rownum COMM,<br />

(mod(rownum,4)+1)*10 DEPTNO<br />

from all_objects<br />

where rownum < 10000<br />

call count cpu elapsed disk query current rows<br />

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

Parse 1 0.03 0.06 0 0 0 0<br />

Execute 1 0.46 0.43 0 15439 948 9999

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

Saved successfully!

Ooh no, something went wrong!