Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 13 ■ PARTITIONING 587 | SELECT STATEMENT | | 1 | | | | PARTITION RANGE ALL | | 1 | 1 | 2 | | TABLE ACCESS BY LOCAL INDEX ROWID| PARTITIONED_TABLE | 1 | 1 | 2 | | INDEX RANGE SCAN | LOCAL_NONPREFIXED | 1 | 1 | 2 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("B"=1) Here the optimizer was not able to remove PART_2 of LOCAL_NONPREFIXED from consideration—it needed to look in both the PART_1 and PART_2 partitions of the index to see if B=1 was in there. Herein lies a performance issue with local nonprefixed indexes: they do not make you use the partition key in the predicate as a prefixed index does. It is not that prefixed indexes are better; it’s just that in order to use them, you must use a query that allows for partition elimination. If we drop the LOCAL_PREFIXED index and rerun the original successful query as follows: ops$tkyte@ORA10G> drop index local_prefixed; Index dropped. ops$tkyte@ORA10G> select * from partitioned_table where a = 1 and b = 1; A B DATA ---------- ---------- -------------------- 1 1 x it succeeds, but as we’ll see, it used the same index that just a moment ago failed us. The plan shows that Oracle was able to employ partition elimination here—the predicate A=1 was enough information for the database to eliminate index partition PART_2 from consideration: ops$tkyte@ORA10G> delete from plan_table; 4 rows deleted. ops$tkyte@ORA10G> explain plan for 2 select * from partitioned_table where a = 1 and b = 1; Explained. ops$tkyte@ORA10G> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------- | Operation | Name | Rows | Pstart| Pstop | ---------------------------------------------------------------------------------- | SELECT STATEMENT | | 1 | | | | PARTITION RANGE SINGLE | | 1 | 1 | 1 | | TABLE ACCESS BY LOCAL INDEX ROWID| PARTITIONED_TABLE | 1 | 1 | 1 | | INDEX RANGE SCAN | LOCAL_NONPREFIXED | 1 | 1 | 1 | ----------------------------------------------------------------------------------
588 CHAPTER 13 ■ PARTITIONING Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("A"=1) 3 - access("B"=1) Note the PSTART and PSTOP column values of 1 and 1.This proves that the optimizer is able to perform partition elimination even for nonprefixed local indexes. If you frequently query the preceding table with the following queries: select ... from partitioned_table where a = :a and b = :b; select ... from partitioned_table where b = :b; then you might consider using a local nonprefixed index on (b,a). That index would be useful for both of the preceding queries. The local prefixed index on (a,b) would be useful only for the first query. The bottom line here is that you should not be afraid of nonprefixed indexes or consider them to be major performance inhibitors. If you have many queries that could benefit from a nonprefixed index as outlined previously, then you should consider using one. The main concern is to ensure that your queries contain predicates that allow for index partition elimination whenever possible. The use of prefixed local indexes enforces that consideration. The use of nonprefixed indexes does not. Consider also how the index will be used. If it will be used as the first step in a query plan, there are not many differences between the two types of indexes. Local Indexes and Unique Constraints To enforce uniqueness—and that includes a UNIQUE constraint or PRIMARY KEY constraints— your partitioning key must be included in the constraint itself if you want to use a local index to enforce the constraint. This is the largest limitation of a local index, in my opinion. Oracle enforces uniqueness only within an index partition—never across partitions. What this implies, for example, is that you cannot range partition on a TIMESTAMP field and have a primary key on the ID that is enforced using a locally partitioned index. Oracle will instead utilize a global index to enforce uniqueness. In the next example, we will create a range partitioned table that is partitioned by a column named LOAD_TYPE, but has a primary key on the ID column. We can do that by executing the following CREATE TABLE statement in a schema that owns no other objects, so we can easily see exactly what objects are created by looking at every segment this user owns: ops$tkyte@ORA10G> CREATE TABLE partitioned 2 ( load_date date, 3 id int, 4 constraint partitioned_pk primary key(id) 5 ) 6 PARTITION BY RANGE (load_date) 7 ( 8 PARTITION part_1 VALUES LESS THAN 9 ( to_date('01/01/2000','dd/mm/yyyy') ) , 10 PARTITION part_2 VALUES LESS THAN 11 ( to_date('01/01/2001','dd/mm/yyyy') )
- Page 582 and 583: CHAPTER 12 ■ DATATYPES 537 ops$tk
- Page 584 and 585: CHAPTER 12 ■ DATATYPES 539 ops$tk
- Page 586 and 587: CHAPTER 12 ■ DATATYPES 541 suppor
- Page 588 and 589: CHAPTER 12 ■ DATATYPES 543 Concep
- Page 590 and 591: CHAPTER 12 ■ DATATYPES 545 We can
- Page 592 and 593: CHAPTER 12 ■ DATATYPES 547 buffer
- Page 594 and 595: CHAPTER 12 ■ DATATYPES 549 Note t
- Page 596 and 597: CHAPTER 12 ■ DATATYPES 551 13 dbm
- Page 598 and 599: CHAPTER 12 ■ DATATYPES 553 equall
- Page 600 and 601: CHAPTER 12 ■ DATATYPES 555 ROWID/
- 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 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 652 and 653: CHAPTER 13 ■ PARTITIONING 607 On
- 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
588<br />
CHAPTER 13 ■ PARTITIONING<br />
Predicate Information (identified by operation id):<br />
---------------------------------------------------<br />
2 - filter("A"=1)<br />
3 - access("B"=1)<br />
Note the PSTART <strong>and</strong> PSTOP column values of 1 <strong>and</strong> 1.This proves that the optimizer is able<br />
to perform partition elimination even for nonprefixed local indexes.<br />
If you frequently query the preceding table with the following queries:<br />
select ... from partitioned_table where a = :a <strong>and</strong> b = :b;<br />
select ... from partitioned_table where b = :b;<br />
then you might consider using a local nonprefixed index on (b,a). That index would be useful<br />
for both of the preceding queries. The local prefixed index on (a,b) would be useful only for the<br />
first query.<br />
The bottom line here is that you should not be afraid of nonprefixed indexes or consider<br />
them to be major performance inhibitors. If you have many queries that could benefit from<br />
a nonprefixed index as outlined previously, then you should consider using one. The main<br />
concern is to ensure that your queries contain predicates that allow for index partition elimination<br />
whenever possible. The use of prefixed local indexes enforces that consideration. The<br />
use of nonprefixed indexes does not. Consider also how the index will be used. If it will be<br />
used as the first step in a query plan, there are not many differences between the two types<br />
of indexes.<br />
Local Indexes <strong>and</strong> Unique Constraints<br />
To enforce uniqueness—<strong>and</strong> that includes a UNIQUE constraint or PRIMARY KEY constraints—<br />
your partitioning key must be included in the constraint itself if you want to use a local index<br />
to enforce the constraint. This is the largest limitation of a local index, in my opinion. <strong>Oracle</strong><br />
enforces uniqueness only within an index partition—never across partitions. What this<br />
implies, for example, is that you cannot range partition on a TIMESTAMP field <strong>and</strong> have a primary<br />
key on the ID that is enforced using a locally partitioned index. <strong>Oracle</strong> will instead utilize<br />
a global index to enforce uniqueness.<br />
In the next example, we will create a range partitioned table that is partitioned by a column<br />
named LOAD_TYPE, but has a primary key on the ID column. We can do that by executing<br />
the following CREATE TABLE statement in a schema that owns no other objects, so we can easily<br />
see exactly what objects are created by looking at every segment this user owns:<br />
ops$tkyte@ORA10G> CREATE TABLE partitioned<br />
2 ( load_date date,<br />
3 id int,<br />
4 constraint partitioned_pk primary key(id)<br />
5 )<br />
6 PARTITION BY RANGE (load_date)<br />
7 (<br />
8 PARTITION part_1 VALUES LESS THAN<br />
9 ( to_date('01/01/2000','dd/mm/yyyy') ) ,<br />
10 PARTITION part_2 VALUES LESS THAN<br />
11 ( to_date('01/01/2001','dd/mm/yyyy') )