Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 10 ■ DATABASE TABLES 385 the cluster at a time (you have to DROP the existing table in a single table hash cluster before you can create another). Additionally, if there is a one-to-one mapping between hash keys and data rows, the access to the rows is somewhat faster as well. These hash clusters are designed for those occasions when you want to access a table by primary key and do not care to cluster other tables with it. If you need fast access to an employee record by EMPNO, a single table hash cluster might be called for. I did the preceding test on a single table hash cluster as well and found the performance to be even better than just a hash cluster. You could even go a step further with this example, however, and take advantage of the fact that Oracle will allow you to write your own specialized hash function (instead of using the default one provided by Oracle). You are limited to using only the columns available in the table, and you may use only the Oracle built-in functions (e.g., no PL/SQL code) when writing these hash functions. By taking advantage of the fact that OBJECT_ID is a number between 1 and 75,000 in the preceding example, I made my “hash function” simply be the OBJECT_ID column itself. In this fashion, I am guaranteed to never have a hash collision. Putting it all together, I’ll create a single table hash cluster with my own hash function via ops$tkyte@ORA10GR1> create cluster hash_cluster 2 ( hash_key number(10) ) 3 hashkeys 75000 4 size 150 5 single table 6 hash is HASH_KEY 7 / Cluster created. I’ve simply added the key words SINGLE TABLE to make it a single table hash cluster. My HASH IS function is simply the HASH_KEY cluster key in this case. This is a SQL function, so I could have used trunc(mod(hash_key/324+278,555)/abs(hash_key+1)) if I wanted (not that this is a good hash function—it just demonstrates that we can use a complex function there if we wish). I used a NUMBER(10) instead of just a number. Since the hash value must be an integer, it cannot have any fractional components. Then, I create the table in that cluster ops$tkyte@ORA10GR1> create table t_hashed 2 cluster hash_cluster(object_id) 3 as 4 select OWNER, OBJECT_NAME, SUBOBJECT_NAME, 5 cast( OBJECT_ID as number(10) ) object_id, 6 DATA_OBJECT_ID, OBJECT_TYPE, CREATED, 7 LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY, 8 GENERATED, SECONDARY 9 from all_objects 10 / Table created. to build the hashed table. Note the use of the CAST built-in function to make the datatype of OBJECT_ID be what it must be. I ran the test as before (three runs of each block), and this time the runstats output was consistently even more positive:
386 CHAPTER 10 ■ DATABASE TABLES Run1 ran in 224 hsecs Run2 ran in 269 hsecs run 1 ran in 83.27% of the time Name Run1 Run2 Diff STAT...index fetch by key 0 48,178 48,178 STAT...buffer is not pinned co 48,178 96,356 48,178 STAT...table fetch by rowid 0 48,178 48,178 STAT...cluster key scans 48,178 0 -48,178 STAT...session logical reads 48,889 145,245 96,356 STAT...consistent gets 48,184 144,540 96,356 STAT...consistent gets from ca 48,184 144,540 96,356 STAT...consistent gets - exami 48,184 144,540 96,356 LATCH.cache buffers chains 51,663 148,019 96,356 Run1 latches total versus runs -- difference and pct Run1 Run2 Diff Pct 298,987 402,085 103,098 74.36% PL/SQL procedure successfully completed. This single table hash cluster required even less latching into the buffer cache to process (it can stop looking for data sooner, and it has more information). As a result, the TKPROF report shows a measurable decrease in CPU utilization this time around: SELECT * FROM T_HASHED WHERE OBJECT_ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 48178 4.45 4.52 0 2 0 0 Fetch 48178 0.67 0.82 0 48178 0 48178 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 96357 5.12 5.35 0 48180 0 48178 Rows Row Source Operation ------- --------------------------------------------------- 48178 TABLE ACCESS HASH T_HASHED (cr=48178 pr=0 pw=0 time=551123 us) ******************************************************************************** SELECT * FROM T_HEAP WHERE OBJECT_ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 48178 5.38 4.99 0 0 0 0 Fetch 48178 1.25 1.65 0 144534 0 48178 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 96357 6.63 6.65 0 144534 0 48178
- Page 379 and 380: 334 CHAPTER 9 ■ REDO AND UNDO Tha
- Page 381 and 382: 336 CHAPTER 9 ■ REDO AND UNDO tou
- Page 383 and 384: 338 CHAPTER 10 ■ DATABASE TABLES
- Page 385 and 386: 340 CHAPTER 10 ■ DATABASE TABLES
- Page 387 and 388: 342 CHAPTER 10 ■ DATABASE TABLES
- Page 389 and 390: 344 CHAPTER 10 ■ DATABASE TABLES
- Page 391 and 392: 346 CHAPTER 10 ■ DATABASE TABLES
- Page 393 and 394: 348 CHAPTER 10 ■ DATABASE TABLES
- Page 395 and 396: 350 CHAPTER 10 ■ DATABASE TABLES
- Page 397 and 398: 352 CHAPTER 10 ■ DATABASE TABLES
- Page 399 and 400: 354 CHAPTER 10 ■ DATABASE TABLES
- Page 401 and 402: 356 CHAPTER 10 ■ DATABASE TABLES
- Page 403 and 404: 358 CHAPTER 10 ■ DATABASE TABLES
- Page 405 and 406: 360 CHAPTER 10 ■ DATABASE TABLES
- Page 407 and 408: 362 CHAPTER 10 ■ DATABASE TABLES
- Page 409 and 410: 364 CHAPTER 10 ■ DATABASE TABLES
- Page 411 and 412: 366 CHAPTER 10 ■ DATABASE TABLES
- Page 413 and 414: 368 CHAPTER 10 ■ DATABASE TABLES
- Page 415 and 416: 370 CHAPTER 10 ■ DATABASE TABLES
- Page 417 and 418: 372 CHAPTER 10 ■ DATABASE TABLES
- Page 419 and 420: 374 CHAPTER 10 ■ DATABASE TABLES
- Page 421 and 422: 376 CHAPTER 10 ■ DATABASE TABLES
- Page 423 and 424: 378 CHAPTER 10 ■ DATABASE TABLES
- Page 425 and 426: 380 CHAPTER 10 ■ DATABASE TABLES
- Page 427 and 428: 382 CHAPTER 10 ■ DATABASE TABLES
- Page 429: 384 CHAPTER 10 ■ DATABASE TABLES
- Page 433 and 434: 388 CHAPTER 10 ■ DATABASE TABLES
- Page 435 and 436: 390 CHAPTER 10 ■ DATABASE TABLES
- Page 437 and 438: 392 CHAPTER 10 ■ DATABASE TABLES
- Page 439 and 440: 394 CHAPTER 10 ■ DATABASE TABLES
- Page 441 and 442: 396 CHAPTER 10 ■ DATABASE TABLES
- Page 443 and 444: 398 CHAPTER 10 ■ DATABASE TABLES
- Page 445 and 446: 400 CHAPTER 10 ■ DATABASE TABLES
- Page 447 and 448: 402 CHAPTER 10 ■ DATABASE TABLES
- Page 449 and 450: 404 CHAPTER 10 ■ DATABASE TABLES
- Page 451 and 452: 406 CHAPTER 10 ■ DATABASE TABLES
- Page 453 and 454: 408 CHAPTER 10 ■ DATABASE TABLES
- 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 ( (++
CHAPTER 10 ■ DATABASE TABLES 385<br />
the cluster at a time (you have to DROP the existing table in a single table hash cluster before<br />
you can create another). Additionally, if there is a one-to-one mapping between hash keys <strong>and</strong><br />
data rows, the access to the rows is somewhat faster as well. These hash clusters are designed<br />
for those occasions when you want to access a table by primary key <strong>and</strong> do not care to cluster<br />
other tables with it. If you need fast access to an employee record by EMPNO, a single table hash<br />
cluster might be called for. I did the preceding test on a single table hash cluster as well <strong>and</strong><br />
found the performance to be even better than just a hash cluster. You could even go a step further<br />
with this example, however, <strong>and</strong> take advantage of the fact that <strong>Oracle</strong> will allow you to<br />
write your own specialized hash function (instead of using the default one provided by <strong>Oracle</strong>).<br />
You are limited to using only the columns available in the table, <strong>and</strong> you may use only the<br />
<strong>Oracle</strong> built-in functions (e.g., no PL/SQL code) when writing these hash functions. By taking<br />
advantage of the fact that OBJECT_ID is a number between 1 <strong>and</strong> 75,000 in the preceding example,<br />
I made my “hash function” simply be the OBJECT_ID column itself. In this fashion, I am<br />
guaranteed to never have a hash collision. Putting it all together, I’ll create a single table hash<br />
cluster with my own hash function via<br />
ops$tkyte@ORA10GR1> create cluster hash_cluster<br />
2 ( hash_key number(10) )<br />
3 hashkeys 75000<br />
4 size 150<br />
5 single table<br />
6 hash is HASH_KEY<br />
7 /<br />
Cluster created.<br />
I’ve simply added the key words SINGLE TABLE to make it a single table hash cluster. My<br />
HASH IS function is simply the HASH_KEY cluster key in this case. This is a SQL function, so I<br />
could have used trunc(mod(hash_key/324+278,555)/abs(hash_key+1)) if I wanted (not that<br />
this is a good hash function—it just demonstrates that we can use a complex function there if<br />
we wish). I used a NUMBER(10) instead of just a number. Since the hash value must be an integer,<br />
it cannot have any fractional components. Then, I create the table in that cluster<br />
ops$tkyte@ORA10GR1> create table t_hashed<br />
2 cluster hash_cluster(object_id)<br />
3 as<br />
4 select OWNER, OBJECT_NAME, SUBOBJECT_NAME,<br />
5 cast( OBJECT_ID as number(10) ) object_id,<br />
6 DATA_OBJECT_ID, OBJECT_TYPE, CREATED,<br />
7 LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY,<br />
8 GENERATED, SECONDARY<br />
9 from all_objects<br />
10 /<br />
Table created.<br />
to build the hashed table. Note the use of the CAST built-in function to make the datatype of<br />
OBJECT_ID be what it must be. I ran the test as before (three runs of each block), <strong>and</strong> this time<br />
the runstats output was consistently even more positive: