Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 13 ■ PARTITIONING 607 On many occasions, I’ve seen that the implementation team will see they have a large table, say of 10 million rows. Now, 10 million sounds like an incredibly large number (and five or ten years ago, it would have been, but time changes all things). The team decides to partition the data. But in looking at the data, there are no logical attributes that make sense for RANGE partitioning. There are no sensible attributes for that. Likewise, LIST partitioning doesn’t make sense. Nothing pops out of this table as being the “right thing” to partition by. So, the team opts for hash partitioning on the primary key, which just happens to be populated by an Oracle sequence number. It looks perfect, it is unique and easy to hash, and many queries are of the form SELECT * FROM T WHERE PRIMARY_KEY = :X. But the problem is, there are many other queries against this object that are not of that form. For illustrative purposes, assume the table in question is really the ALL_OBJECTS dictionary view, and while internally many queries would be of the form WHERE OBJECT_ID = :X, the end users frequently have these requests of the application as well: • Show me the details of SCOTT’s EMP TABLE (WHERE OWNER = :O AND OBJECT_TYPE = :T ➥ AND OBJECT_NAME = :N). • Show me all of the tables SCOTT owns (WHERE OWNER = :O AND OBJECT_TYPE = :T). • Show me all of the objects SCOTT owns (WHERE OWNER = :O). In support of those queries, you have an index on (OWNER,OBJECT_TYPE,OBJECT_NAME). But you also read that “local indexes are more available,” and you would like to be more available regarding your system, so you implement with them. You end up re-creating your table like this, with 16 hash partitions: ops$tkyte@ORA10G> create table t 2 ( OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID, 3 OBJECT_TYPE, CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS, 4 TEMPORARY, GENERATED, SECONDARY ) 5 partition by hash(object_id) 6 partitions 16 7 as 8 select * from all_objects; Table created. ops$tkyte@ORA10G> create index t_idx 2 on t(owner,object_type,object_name) 3 LOCAL 4 / Index created. ops$tkyte@ORA10G> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', cascade=>true); 4 end; 5 / PL/SQL procedure successfully completed.
608 CHAPTER 13 ■ PARTITIONING and you execute your typical OLTP queries you know you run frequently: variable o varchar2(30) variable t varchar2(30) variable n varchar2(30) exec :o := 'SCOTT'; :t := 'TABLE'; :n := 'EMP'; select * from t where owner = :o and object_type = :t and object_name = :n / select * from t where owner = :o and object_type = :t / select * from t where owner = :o / but you notice that when you run this with SQL_TRACE=TRUE and review the resulting TKPROF report the following performance characteristics: select * from t where owner = :o and object_type = :t and object_name = :n call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 4 0.00 0.00 0 34 0 1 Rows Row Source Operation ------- --------------------------------------------------- 1 PARTITION HASH ALL PARTITION: 1 16 (cr=34 pr=0 pw=0 time=359 us) 1 TABLE ACCESS BY LOCAL INDEX ROWID T PARTITION: 1 16 (cr=34 pr=0 1 INDEX RANGE SCAN T_IDX PARTITION: 1 16 (cr=33 pr=0 pw=0 time=250 You compare that to the same table, only with no partitioning implemented, and discover the following: select * from t where owner = :o and object_type = :t and object_name = :n call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 4 0.00 0.00 0 5 0 1
- Page 602 and 603: CHAPTER 13 ■ ■ ■ Partitioning
- Page 604 and 605: CHAPTER 13 ■ PARTITIONING 559 6 (
- Page 606 and 607: CHAPTER 13 ■ PARTITIONING 561 els
- Page 608 and 609: CHAPTER 13 ■ PARTITIONING 563 BIG
- Page 610 and 611: CHAPTER 13 ■ PARTITIONING 565 Enh
- Page 612 and 613: CHAPTER 13 ■ PARTITIONING 567 Tab
- Page 614 and 615: CHAPTER 13 ■ PARTITIONING 569 tha
- Page 616 and 617: CHAPTER 13 ■ PARTITIONING 571 PAR
- Page 618 and 619: CHAPTER 13 ■ PARTITIONING 573 35
- Page 620 and 621: CHAPTER 13 ■ PARTITIONING 575 If
- Page 622 and 623: CHAPTER 13 ■ PARTITIONING 577 We
- Page 624 and 625: CHAPTER 13 ■ PARTITIONING 579 14
- Page 626 and 627: CHAPTER 13 ■ PARTITIONING 581 ops
- Page 628 and 629: CHAPTER 13 ■ PARTITIONING 583 In
- Page 630 and 631: CHAPTER 13 ■ PARTITIONING 585 ops
- Page 632 and 633: CHAPTER 13 ■ PARTITIONING 587 | S
- Page 634 and 635: CHAPTER 13 ■ PARTITIONING 589 12
- Page 636 and 637: CHAPTER 13 ■ PARTITIONING 591 ops
- Page 638 and 639: CHAPTER 13 ■ PARTITIONING 593 •
- Page 640 and 641: CHAPTER 13 ■ PARTITIONING 595 Now
- Page 642 and 643: CHAPTER 13 ■ PARTITIONING 597 the
- Page 644 and 645: CHAPTER 13 ■ PARTITIONING 599 imp
- Page 646 and 647: CHAPTER 13 ■ PARTITIONING 601 OLT
- Page 648 and 649: CHAPTER 13 ■ PARTITIONING 603 5 s
- Page 650 and 651: CHAPTER 13 ■ PARTITIONING 605 Sur
- Page 654 and 655: CHAPTER 13 ■ PARTITIONING 609 Row
- Page 656 and 657: CHAPTER 13 ■ PARTITIONING 611 So,
- Page 658 and 659: CHAPTER 13 ■ PARTITIONING 613 Aud
- Page 660 and 661: CHAPTER 14 ■ ■ ■ Parallel Exe
- Page 662 and 663: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 664 and 665: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 666 and 667: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 668 and 669: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 670 and 671: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 672 and 673: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 674 and 675: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 676 and 677: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 678 and 679: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 680 and 681: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 682 and 683: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 684 and 685: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 686 and 687: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 688 and 689: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 690 and 691: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 692 and 693: CHAPTER 14 ■ PARALLEL EXECUTION 6
- Page 694 and 695: CHAPTER 15 ■ ■ ■ Data Loading
- Page 696 and 697: CHAPTER 15 ■ DATA LOADING AND UNL
- Page 698 and 699: CHAPTER 15 ■ DATA LOADING AND UNL
- Page 700 and 701: CHAPTER 15 ■ DATA LOADING AND UNL
CHAPTER 13 ■ PARTITIONING 607<br />
On many occasions, I’ve seen that the implementation team will see they have a large<br />
table, say of 10 million rows. Now, 10 million sounds like an incredibly large number (<strong>and</strong> five<br />
or ten years ago, it would have been, but time changes all things). The team decides to partition<br />
the data. But in looking at the data, there are no logical attributes that make sense for<br />
RANGE partitioning. There are no sensible attributes for that. Likewise, LIST partitioning doesn’t<br />
make sense. Nothing pops out of this table as being the “right thing” to partition by. So, the<br />
team opts for hash partitioning on the primary key, which just happens to be populated by an<br />
<strong>Oracle</strong> sequence number. It looks perfect, it is unique <strong>and</strong> easy to hash, <strong>and</strong> many queries are<br />
of the form SELECT * FROM T WHERE PRIMARY_KEY = :X.<br />
But the problem is, there are many other queries against this object that are not of that<br />
form. For illustrative purposes, assume the table in question is really the ALL_OBJECTS dictionary<br />
view, <strong>and</strong> while internally many queries would be of the form WHERE OBJECT_ID = :X, the<br />
end users frequently have these requests of the application as well:<br />
• Show me the details of SCOTT’s EMP TABLE (WHERE OWNER = :O AND OBJECT_TYPE = :T ➥<br />
AND OBJECT_NAME = :N).<br />
• Show me all of the tables SCOTT owns (WHERE OWNER = :O AND OBJECT_TYPE = :T).<br />
• Show me all of the objects SCOTT owns (WHERE OWNER = :O).<br />
In support of those queries, you have an index on (OWNER,OBJECT_TYPE,OBJECT_NAME). But<br />
you also read that “local indexes are more available,” <strong>and</strong> you would like to be more available<br />
regarding your system, so you implement with them. You end up re-creating your table like<br />
this, with 16 hash partitions:<br />
ops$tkyte@ORA10G> create table t<br />
2 ( OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID,<br />
3 OBJECT_TYPE, CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS,<br />
4 TEMPORARY, GENERATED, SECONDARY )<br />
5 partition by hash(object_id)<br />
6 partitions 16<br />
7 as<br />
8 select * from all_objects;<br />
Table created.<br />
ops$tkyte@ORA10G> create index t_idx<br />
2 on t(owner,object_type,object_name)<br />
3 LOCAL<br />
4 /<br />
Index created.<br />
ops$tkyte@ORA10G> begin<br />
2 dbms_stats.gather_table_stats<br />
3 ( user, 'T', cascade=>true);<br />
4 end;<br />
5 /<br />
PL/SQL procedure successfully completed.