Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
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.
- Page 455 and 456: 410 CHAPTER 10 ■ DATABASE TABLES
- Page 457 and 458: 412 CHAPTER 10 ■ DATABASE TABLES
- Page 459 and 460: 414 CHAPTER 10 ■ DATABASE TABLES
- Page 461 and 462: 416 CHAPTER 10 ■ DATABASE TABLES
- Page 463 and 464: 418 CHAPTER 10 ■ DATABASE TABLES
- Page 466 and 467: CHAPTER 11 ■ ■ ■ Indexes Inde
- Page 468 and 469: CHAPTER 11 ■ INDEXES 423 value of
- Page 470 and 471: CHAPTER 11 ■ INDEXES 425 One of t
- Page 472 and 473: CHAPTER 11 ■ INDEXES 427 We then
- Page 474 and 475: CHAPTER 11 ■ INDEXES 429 we ended
- Page 476 and 477: CHAPTER 11 ■ INDEXES 431 The data
- Page 478 and 479: CHAPTER 11 ■ INDEXES 433 if ( (++
- Page 480 and 481: CHAPTER 11 ■ INDEXES 435 Table 11
- Page 482 and 483: CHAPTER 11 ■ INDEXES 437 When Sho
- Page 484 and 485: CHAPTER 11 ■ INDEXES 439 an 8KB b
- Page 486 and 487: CHAPTER 11 ■ INDEXES 441 select *
- Page 488 and 489: CHAPTER 11 ■ INDEXES 443 select *
- Page 490 and 491: CHAPTER 11 ■ INDEXES 445 Indicate
- Page 492 and 493: CHAPTER 11 ■ INDEXES 447 an index
- Page 494 and 495: CHAPTER 11 ■ INDEXES 449 Table 11
- Page 496 and 497: CHAPTER 11 ■ INDEXES 451 9 1, 'M'
- Page 498 and 499: CHAPTER 11 ■ INDEXES 453 column w
- Page 500 and 501: CHAPTER 11 ■ INDEXES 455 Bitmap j
- Page 502 and 503: CHAPTER 11 ■ INDEXES 457 INSERT a
- Page 504 and 505: CHAPTER 11 ■ INDEXES 459 7 l_last
- Page 508 and 509: CHAPTER 11 ■ INDEXES 463 If we co
- Page 510 and 511: CHAPTER 11 ■ INDEXES 465 ops$tkyt
- Page 512 and 513: CHAPTER 11 ■ INDEXES 467 Caveat o
- Page 514 and 515: CHAPTER 11 ■ INDEXES 469 ops$tkyt
- Page 516 and 517: CHAPTER 11 ■ INDEXES 471 Frequent
- Page 518 and 519: CHAPTER 11 ■ INDEXES 473 select *
- Page 520 and 521: CHAPTER 11 ■ INDEXES 475 If you s
- Page 522 and 523: CHAPTER 11 ■ INDEXES 477 we’ll
- Page 524 and 525: CHAPTER 11 ■ INDEXES 479 Predicat
- Page 526 and 527: CHAPTER 11 ■ INDEXES 481 ops$tkyt
- Page 528 and 529: CHAPTER 11 ■ INDEXES 483 ops$tkyt
- Page 530 and 531: CHAPTER 11 ■ INDEXES 485 This dem
- Page 532 and 533: CHAPTER 11 ■ INDEXES 487 SELECT /
- Page 534 and 535: CHAPTER 12 ■ ■ ■ Datatypes Ch
- Page 536 and 537: CHAPTER 12 ■ DATATYPES 491 • TI
- Page 538 and 539: CHAPTER 12 ■ DATATYPES 493 (in th
- Page 540 and 541: CHAPTER 12 ■ DATATYPES 495 That d
- Page 542 and 543: CHAPTER 12 ■ DATATYPES 497 ops$tk
- Page 544 and 545: CHAPTER 12 ■ DATATYPES 499 Table
- Page 546 and 547: CHAPTER 12 ■ DATATYPES 501 The IN
- Page 548 and 549: CHAPTER 12 ■ DATATYPES 503 ops$tk
- Page 550 and 551: CHAPTER 12 ■ DATATYPES 505 • BI
- Page 552 and 553: CHAPTER 12 ■ DATATYPES 507 NUMBER
- Page 554 and 555: CHAPTER 12 ■ DATATYPES 509 MSG NU
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