working with partitioned tables the unpleasant details brian hitchcock ocp 8, 8i, 9i dba sun...

82
Working with Partitioned Tables The Unpleasant Details Brian Hitchcock OCP 8, 8i, 9i DBA Sun Microsystems [email protected] [email protected] NoCOUG Brian Hitchcock November 13, 2003 Page 1

Upload: silvia-morgan

Post on 02-Jan-2016

222 views

Category:

Documents


1 download

TRANSCRIPT

Working with Partitioned Tables The Unpleasant Details

Brian HitchcockOCP 8, 8i, 9i DBA

Sun Microsystems

[email protected]

[email protected]

Brian Hitchcock November 13, 2003 Page 1

NoCOUG

Brian Hitchcock November 13, 2003 Page 2

The Application – History

Website click-stream data Large amounts of data generated daily Users want to access data immediately

– Next day was acceptable Users want 14 months of data on-line Performance was poor

– Huge tables, millions of rows– Inserting new data very slow– Retrieving data very slow– User generate SQL

No controls on the quality of the SQL

NoCOUG

Brian Hitchcock November 13, 2003 Page 3

Application – How it Works

Website click-stream log files sent to db machine daily Database tables setup with weekly partitioning

– 3 main tables– Range partitioning on date

New data added to db once per day– Load must finish before users can access each day– Failure of load process, users can’t work, must load 2 days

data to catch up

NoCOUG

Brian Hitchcock November 13, 2003 Page 4

The Application – Act I

Consultants setup weekly partitioning– Vendor scripts all setup for daily partitioning– We had to setup our own maintenance scripts

Once per week Create new week partition oldest weekly partition

Analyze new partition– Vendor tech support won’t support weekly partitioning

14 months of data on-line, 60 weekly partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 5

The Application – Act II

The Consultant Is Gone (call Brian…) Vendor wants us to upgrade

– New app version is much faster (“trust us”)– Need to move to daily partitioning

Users agree to 7 months data online– Improve performance overall (less total data)

We can use vendor’s maintenance scripts– Reduced headcount makes our scripts hard to maintain– Easier to get support from vendor tech support

NoCOUG

Brian Hitchcock November 13, 2003 Page 6

Project Phases

As I inherited the project– 14 months weekly partitions

Vendor Upgrade I– Move to daily partitioning– Drop 7 months of data

Vendor Upgrade II– Required renaming of index partitions

Must match table partition names

NoCOUG

Brian Hitchcock November 13, 2003 Page 7

What Are Partitioned Tables? Single Big Table

– Many sub-tables (partitions) Each partition

– Acts like a separate table– Index (partition) for each partition

Performance– Load to separate partition(s)– Analyze each partition separately– SQL can be satisfied with small subset of entire table

Partition pruning Answer to a Jeopardy question

– Alternate term for DBA job security…

NoCOUG

Brian Hitchcock November 13, 2003 Page 8

Why Use Them?

Performance Faster SQL

– Partition Pruning

Faster Analyze– Only analyze single partition

Logical– Application data is ‘partitioned’ (weekly for this case)– Easy to add new partition, drop oldest partition

Faster than deleting some rows from larger table Faster than inserting new rows into larger table

NoCOUG

Brian Hitchcock November 13, 2003 Page 9

Partitioned Table

Data Data 10/01/2003

Data Data 09/01/2003

Data Data 08/01/2003

Data Data 07/01/2003

Data Data 06/01/2003

Data Data 05/01/2003

Data Data 04/01/2003

Data Data 03/01/2003

Data Data 02/01/2003

Data Data 01/01/2003

Data Data 12/01/2002

Data Data 11/01/2002

Data Data 10/01/2003

Data Data 09/01/2003

Data Data 08/01/2003

Data Data 07/01/2003

Data Data 06/01/2003

Data Data 05/01/2003

Data Data 04/01/2003

Data Data 03/01/2003

Data Data 02/01/2003

Data Data 01/01/2003

Data Data 12/01/2002

Data Data 11/01/2002

Non-PartitionedPartitioned

Column A Column B Date

Column A Column B Date

Column A Column B Date

Column A Column B Date

Table_1Table_1 Partition_1

Table_1 Partition_2

Table_1 Partition_3

NoCOUG

Brian Hitchcock November 13, 2003 Page 10

Inserting Data

Oracle examines– Inserted value of column used for range

partitioning– High_value of existing partitions

Rows with values greater than or equal to the highest high_value

– If MAXVALUE used, inserted– If MAXVALUE not used, rejected

NoCOUG

Brian Hitchcock November 13, 2003 Page 11

Partitioned Table

Data Data 10/01/2003

Data Data 09/01/2003

Data Data 08/01/2003

Data Data 07/01/2003

Data Data 06/01/2003

Data Data 05/01/2003

Data Data 04/01/2003

Data Data 03/01/2003

Data Data 02/01/2003

Data Data 01/01/2003

Data Data 12/01/2002

Data Data 11/01/2002

Partitioned

Column A Column B Date

Column A Column B Date

Column A Column B Date

Partition_1 high_value 10/02/2003

Partition_2 high_value 06/02/2003

Partition_3 high_value 02/02/2003

Values > or = 10/02/2003 rejected

Rows with range values less than 02/02/2003Will be inserted into Partition_3

Rows with range values less than 06/02/2003And > or = 02/02/2003Will be inserted into Partition_2

Rows with range values less than 10/02/2003And > or = to 06/02/2003Will be inserted into Partition_1

NoCOUG

Brian Hitchcock November 13, 2003 Page 12

Partitioned Table MAXVALUE

Data Data 10/01/2003

Data Data 09/01/2003

Data Data 08/01/2003

Data Data 07/01/2003

Data Data 06/01/2003

Data Data 05/01/2003

Data Data 04/01/2003

Data Data 03/01/2003

Data Data 02/01/2003

Data Data 01/01/2003

Data Data 12/01/2002

Data Data 11/01/2002

Partitioned – Using MAXVALUE

Column A Column B Date

Column A Column B Date

Column A Column B Date

Partition 1 high_value MAXVALUE

Partition 2 high_value 06/02/2003

Partition 3 high_value 02/02/2003

Rows with range values less than 02/02/2003Will be inserted into Partition 3

Rows with range values less than 06/02/2003Will be inserted into Partition 2

Rows with range values greater than or = to 06/02/2003Will be inserted into Partition 1

NoCOUG

Brian Hitchcock November 13, 2003 Page 13

Partitioning -- Example

CREATE TABLE stock_xactions

(stock_symbol CHAR(5), stock_series CHAR(1), num_shares NUMBER(10), price NUMBER(5,2),

trade_date DATE)

STORAGE (INITIAL 100K NEXT 50K) LOGGING

PARTITION BY RANGE (trade_date)

(PARTITION sx1992 VALUES LESS THAN (TO_DATE('01-JAN-1993','DD-MON-YYYY')),

PARTITION sx1993 VALUES LESS THAN (TO_DATE('01-JAN-1994','DD-MON-YYYY')),

PARTITION sx1994 VALUES LESS THAN (MAXVALUE));

select * from user_tab_partitions;

TABLE_NAME COM PARTITION_NAME SUBPARTITI HIGH_VALUE

----------------------------- --- ------------------------------ ---------- ---------------------------------------------------------------------

STOCK_XACTIONS N0 SX1992 0 TO_DATE(' 1993-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',

STOCK_XACTIONS N0 SX1993 0 TO_DATE(' 1994-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',

STOCK_XACTIONS N0 SX1994 0 MAXVALUE

3 rows selected.

NoCOUG

Brian Hitchcock November 13, 2003 Page 14

Weekly Partition Maintenance Once per week

– Compute high_value of next weekly partition– Create partition for next week– Drop oldest weekly partition– Rebuild indexes

Or drop and recreate indexes– Load data into new partition

Analyze new partition

Once loaded, analyzed– Maximum read performance– Partition no longer has inserts or analyze

NoCOUG

Brian Hitchcock November 13, 2003 Page 15

Weekly Maintenance New

Latest

Oldest

Add New Partition – Load Data, Analyze

Drop Oldest Partition

Existing Partitions

Existing Partitions

14 months, 60 weekly partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 16

Partitions and Tablespaces For performance, want to control placement of

partitions– New, data being loaded– Existing, data being retrieved

Use separate tablespace for each partition Spread across 8 filesystems (8 sets of disks)

– Tablespaces of sequential table partitions– Same for index partitions but offset– Corresponding table/index partitions always on

different filesystems

NoCOUG

Brian Hitchcock November 13, 2003 Page 17

Partitions and Tablespaces

Design Goal– Partition being loaded, analyzed

One separate disks from partitions being read Assumes most queries read data from most recent

partitions If partition_1 being loaded

Partitions 2, 3, 4 can be read without conflicting with the load, analyze

– Partitions can be used for fine-grained placement of data on disk

NoCOUG

Brian Hitchcock November 13, 2003 Page 18

A Plug for Certification

I didn’t know anything about Partitioned Tables

Wasn’t planning to use them Memorized what I needed to pass the OCP

exam Within a month…

– Phone rings – “Can you help with partitioned tables?”

NoCOUG

Brian Hitchcock November 13, 2003 Page 19

Types of Partitioning

Range– Partition based on value of column(s) of table

Date Part Number SSN

Hash– Rows spread evenly across all partitions

List (9i)– Partitions based on user-specified lists of values

NoCOUG

Brian Hitchcock November 13, 2003 Page 20

Partitioning Details

Create partitions– You need to name each partition– If you don’t

Oracle names them for you The names are not easy to use

Many other partitioning possibilities– This is not a training course on partitioned tables– Only cover what I actually used– More options with 9i, this was for 8i

NoCOUG

Brian Hitchcock November 13, 2003 Page 21

What About Indexes?

Indexes of Partitioned Tables– Local

Index partitions always align with table partitions

– Global Different partitioning from table partitioning

– Become invalid when Add, drop, split existing partitions

– SQL to manually rebuild invalid or unusable index partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 22

Partition Maintenance

Depends on your application May need to handle

– Adding new– Dropping old– Splitting– Analyzing

NoCOUG

Brian Hitchcock November 13, 2003 Page 23

Partitioned Table Info

System Tables– dba_tables– dba_tab_partitions– dba_indexes– dba_ind_partitions– dba_part_key_columns

NoCOUG

Brian Hitchcock November 13, 2003 Page 24

Partitioned Table Info -- SQL Number of partitioned tables

– select count(*) from dba_tables where partitioned='YES';

Name of each partitioned table– select table_name, partitioned from dba_tables where partitioned='YES'

order by table_name;

Number of partitions in each partitioned table– select table_name, count(*) from dba_tab_partitions group by table_name;

Same info for indexes– select count(*) from dba_indexes where partitioned='YES';– select index_name, partitioned from dba_indexes where

partitioned='YES' order by index_name;– select index_name, count(*) from dba_ind_partitions group by

index_name;

NoCOUG

Brian Hitchcock November 13, 2003 Page 25

Partitioned Table Info -- SQL

Number of columns used, range partitioning– select name, object_type, count(*) from

dba_part_key_columns group by name, object_type;

Partitioning key columns– select * from dba_part_key_columns;

Indexes on each partitioned table– select table_name, index_name from dba_indexes where

tablespace_name is NULL order by table_name, index_name;

NoCOUG

Brian Hitchcock November 13, 2003 Page 26

Partitioned Table Info -- SQL

High values for table partitions– set long 30– select table_name, tablespace_name, partition_name,

high_value from dba_tab_partitions order by table_name, tablespace_name, partition_name;

High values for index partitions– set long 30– select index_name, tablespace_name, partition_name,

high_value from dba_ind_partitions order by index_name, tablespace_name, partition_name;

NoCOUG

Brian Hitchcock November 13, 2003 Page 27

Partition Range How to find ‘range’ for each partition?

– Only have high_value for each partition– Have to examine ordered list of high_value– Ranges not stored in system tables

Range of values is dynamic– Range of highest partition changes as higher values

inserted (assumes MAXVALUES used)– Same for lowest partition

Examine ordered list of high_value– Can’t “order by” LONG– high_value is a LONG

NoCOUG

Brian Hitchcock November 13, 2003 Page 28

Partition Range

Need ordered list of high_values Use DBA_TAB_PARTITIONS

– partition_position– High_value

Use partition_position?– Oracle docs only say

position of the partition within the table What exactly does that mean?

NoCOUG

Brian Hitchcock November 13, 2003 Page 29

Partition Range

Partition_Position– Numbers the partitions in order of high_value– Numbers change with each add, split, drop of any

partition– Is dynamic

What to do?– I decided to use high_value because it doesn’t

change as partitions are manipulated– But, can’t “order by”

More on this later…

NoCOUG

Brian Hitchcock November 13, 2003 Page 30

Vendor Upgrade I

Existing 14 months data– Weekly partitions

Reduce to 7 months– Hard to find the correct partitions to drop

Split each weekly partition– 7 daily partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 31

Do the Math

7 Months (36 weeks)– 252 daily partitions– Times 3 tables– Times 11 indexes– Total of 3528 partitions = (756 table + 2772 index)

Vendor upgrade– Requires specific, rigid partition naming scheme– I don’t get to pick the partition names

NoCOUG

Brian Hitchcock November 13, 2003 Page 32

Oracle Utilities? There aren’t any that

– Split tables based on your requirements– Take a set of existing partitions, tablespaces– Move to new partitions, tablespaces

No utility to map from one partitioning to another As the number of partitions grows, so does the work

involved to shift from one partitioning scheme to another

The same things that make partitioned tables so good for performance make them a lot more work to maintain

– Lots of small pieces to maintain

NoCOUG

Brian Hitchcock November 13, 2003 Page 33

Partition Split Issues

When splitting– Need new tablespaces– Have existing tablespaces– Split SQL sends both table and index partitions to same

new tablespace– New tablespace needs to be 2x final size– Separate tablespace for new index partitions– Reclaim disk space from new table partition tablespace

Overall need 3x disk space during splits– Need to reclaim this disk space after move to daily

partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 34

Disk Space Needed

Existing Table Partition TP1 Existing Index Partition IP1

Post-split Table Partition TP1a, Index Partition IP1a

Tablespace Ind1Tablespace Tab1

Tablespace Tab2

Post-split Table Partition TP1b, Index Partition IP1b

Tablespace Ind2

Assuming index partition is same size as table partitionAssumes indexes not dropped before split

Table Partition TP1a Index Partition IP1a Table Partition TP1b Index Partition IP1b

Tablespace Tab3 Tablespace Ind3 Tablespace Tab4 Tablespace Ind4

Need 3x original tablespace (disk space)

Original Partitions

Split Partitions

Separate Table/Index Partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 35

Disk Space Reclaimed

After split, need to reclaim 2/3 of disk space

Existing Table Partition TP1 Existing Index Partition IP1

Post-split Table Partition TP1a, Index Partition IP1a

Tablespace Ind1Tablespace Tab1

Tablespace Tab2

Post-split Table Partition TP1b, Index Partition IP1b

Tablespace Ind2

Table Partition TP1a Index Partition IP1a Table Partition TP1b Index Partition IP1b

Tablespace Tab3 Tablespace Ind3 Tablespace Tab4 Tablespace Ind4

EMPTY TBLSPC

NoCOUG

Brian Hitchcock November 13, 2003 Page 36

Tasks for Daily Partitioning

Create new tablespaces– Single tablespace for partitions of each table– Twice as big as final size– Single tablespace for partitions of each index

Drop existing indexes (weekly partitions)– Total disk space needed reduced from 3x to 2x

Split weekly partitions into daily partitions Re-create indexes

– Daily partitions created automatically Reclaim disk space

NoCOUG

Brian Hitchcock November 13, 2003 Page 37

Move to Daily Partitioning

Reduce to 7 months week partitions– SQL to drop existing weekly partitions simple to generate– Hard to see which partitions to drop since you can’t order

by high_value

Split Existing Weekly Partitions– Generate SQL for one week– Cut/paste/edit for other weekly partitions

Tedious, error prone SQL isn’t consistent enough to make automation

straight-forward 37 weekly partitions need splitting

NoCOUG

Brian Hitchcock November 13, 2003 Page 38

Weekly Partition Split-- existing weekly partition BRH_TAB_PART_5

partition BRH_TAB_PART_5_nexta

partition BRH_TAB_PART_5_nextb

partition BRH_TAB_PART_5_nextc

partition BRH_TAB_PART_5_nextd

BRH_TAB_PART_5_nexte

BRH_TAB_ PART_5_7

BRH_TAB_ PART_5_7

BRH_TAB_ PART_5_6

BRH_TAB_ PART_5_5

BRH_TAB_ PART_5_4

BRH_TAB_ PART_5_3

BRH_TAB_ PART_5_2

BRH_TAB_ PART_5_1

BRH_TAB_ PART_5_3

BRH_TAB_ PART_5_4

BRH_TAB_ PART_5_6

BRH_TAB_ PART_5_5

BRH_TAB_ PART_5_2

BRH_TAB_ PART_5_1

a

High_value = 2003-05-10

High_value = 2003-05-09

High_value = 2003-05-08

High_value = 2003-05-07

High_value = 2003-05-06

High_value = 2003-05-05

High_value = 2003-05-04

2003-05-10

2003-05-09

2003-05-08

2003-05-07

2003-05-06

2003-05-05

NoCOUG

Brian Hitchcock November 13, 2003 Page 39

Weekly Partition Split SQL-- existing weekly partition BRH_TAB_PART_5 TO_DATE('2003-05-10 00:00:00'alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5 at ( TO_DATE('2003-05-09 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_nexta tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_7 tablespace BRH_TAB_TBLSPC);

alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5_nexta at ( TO_DATE('2003-05-08 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_nextb tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_6 tablespace BRH_TAB_TBLSPC);

alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5_nextb at ( TO_DATE('2003-05-07 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_nextc tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_5 tablespace BRH_TAB_TBLSPC);

alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5_nextc at ( TO_DATE('2003-05-06 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_nextd tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_4 tablespace BRH_TAB_TBLSPC);

alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5_nextd at ( TO_DATE('2003-05-05 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_nexte tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_3 tablespace BRH_TAB_TBLSPC);

alter table brhuser.BRH_TABLE split partition BRH_TAB_PART_5_nexte at ( TO_DATE('2003-05-04 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) into (partition BRH_TAB_PART_5_1 tablespace BRH_TAB_TBLSPC, partition BRH_TAB_PART_5_2 tablespace BRH_TAB_TBLSPC);

NoCOUG

Brian Hitchcock November 13, 2003 Page 40

Weekly Partition Split SQL Split partition SQL

– Requires two new partition names– Splits existing partition by specifying new high_value

Existing data moves to two new partitions based on above or below new high_value

If you don’t specify partition names, Oracle names them for you

Oracle partition names are not intuitive…– New partitions move to specified tablespaces– New index partitions also moved to same tablespaces

Need to move new index partitions to separate tablespace(s) as separate step

Or, drop indexes, recreate after all splits done– Indexes split automatically as table splits

NoCOUG

Brian Hitchcock November 13, 2003 Page 41

Vendor Upgrade II

Now that daily partitioning is setup– Vendor upgrade fails– Specific index partition naming requirements– Our home-grown weekly partitioning didn’t meet these

We need this done by Monday…– Labor Day Weekend

I didn’t want to do this manually again Need to rename existing index partition names

– Number in partition name must agree with number in table partition name

Lots of SQL that must be perfect

NoCOUG

Brian Hitchcock November 13, 2003 Page 42

But Wait, There’s More

Vendor had executed a script that partially renamed some partitions

I had to fix this Existing names of these partitions conflicted

with the names I had to generate Vendor upgrade process requires specific

format for daily partition name and number– Where is the Vendor DBA?

NoCOUG

Brian Hitchcock November 13, 2003 Page 43

How to Automate?

What needs to be done– Document existing names of table and index

partitions– Rename index partitions using vendor

requirements– Deal with vendor’s mistakes – some partitions

already renamed– Develop process to deal with this and future

needs

NoCOUG

Brian Hitchcock November 13, 2003 Page 44

SQL

Document existing table and index partition names

Create new names for index partitions– Index partitions use same numbering as

corresponding table partition names

Generate all SQL statements to alter existing index partition names

Document all intermediate steps

NoCOUG

Brian Hitchcock November 13, 2003 Page 45

Details

High Value of each partition stored in LONG column

LONG datatype is special– Can’t be used in any SQL function– Can’t order by etc.– In SQL*Plus, set LONG 100 to see all of

high_value

NoCOUG

Brian Hitchcock November 13, 2003 Page 46

SQL Script

Need to ‘order by’ high value to show existing weekly partitions

– How to get around LONG limitations? Export

LONG is converted to a string Import

Import string into VARCHAR2 column Can ‘order by’ on the VARCHAR2 column

NoCOUG

Brian Hitchcock November 13, 2003 Page 47

Table of Index Partition Names

Store data about existing table and index partitions

Use SQL to create new index partition names within the table

– Flexible– Self-documenting

Select from table to generate SQL for– Changing index partition names– Fix vendor partition naming mistakes

NoCOUG

Brian Hitchcock November 13, 2003 Page 48

For Each Partitioned Index Create table

– Table name– Table tablespace name– Table partition name– Table partition high value (text version)– Index name– Index tablespace name– Index partition name (Existing)– Index partition high value (text version)– Temporary index partition name– Final index partition name (used for renaming)

NoCOUG

Brian Hitchcock November 13, 2003 Page 49

Index Partition Renaming

The process– Spool existing table and index info

Including high_value for each partition– Use SQL*Loader to insert info into table– Use SQL to insert temporary and final index

partition names into table Temporary index partition name deals with any

name conflicts…– Use SQL to generate all SQL statements to

actually change index partition names

NoCOUG

Brian Hitchcock November 13, 2003 Page 50

SQL Used--> generate file of table partitions for BRH_TABLE...

sqlplus -s brhuser@BRH_DB1

spool BRH_TABLE_partition_data_05242003.txt

set long 30

set pagesize 1500

set linesize 150

set echo off

set feedback off

set heading off

select SUBSTR(table_name, 1, 25), ',', SUBSTR(tablespace_name,1,20), ',', SUBSTR(partition_name,1,20),',"', high_value, '"' from dba_tab_partitions where table_name='BRH_TABLE' order by partition_name;

spool off

exit

example data:

BRH_TABLE , BRH_TAB_TBLSPC , BRH_TAB_PART_1 ," TO_DATE(' 2003-05-04 00:00:00' "

BRH_TABLE , BRH_TAB_TBLSPC , BRH_TAB_PART_10 ," TO_DATE(' 2003-05-13 00:00:00' "

BRH_TABLE , BRH_TAB_TBLSPC , BRH_TAB_PART_100 ," TO_DATE(' 2002-12-02 00:00:00' "

BRH_TABLE , BRH_TAB_TBLSPC , BRH_TAB_PART_101 ," TO_DATE(' 2002-12-03 00:00:00' "

BRH_TABLE , BRH_TAB_TBLSPC , BRH_TAB_PART_102 ," TO_DATE(' 2002-12-04 00:00:00' "

NoCOUG

Brian Hitchcock November 13, 2003 Page 51

SQL Used create table to load this data into...

This table holds the existing table partition data ordered by partition name

create table BRH_TABLE_part_data_t

(table_name VARCHAR(30), tab_tblspc_name VARCHAR2(25), tab_part_name VARCHAR2(25), tab_high_value_text VARCHAR2(35));

--> sqlldr control file to load this data...

sqlldr_load_BRH_TABLE_partition_data.ctl

load data

infile BRH_TABLE_partition_data_05242003.txt'

into table BRH_TABLE_part_data_t

fields terminated by ',' optionally enclosed by '"'

(table_name, tab_tblspc_name, tab_part_name, tab_high_value_text)

--> sqlldr command...

sqlldr userid=psycho/psycho control=sqlldr_load_BRH_TABLE_partition_data.ctl log=sqlldr_load_BRH_TABLE_partition_data.log

NoCOUG

Brian Hitchcock November 13, 2003 Page 52

SQL Used--> trim the high_value_text

update BRH_TABLE_part_data_t set tab_high_value_text=SUBSTR(tab_high_value_text,12,19);

commit;

Example data:

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_1 2003-05-04 00:00:00

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_10 2003-05-13 00:00:00

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_100 2002-12-02 00:00:00

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_101 2002-12-03 00:00:00

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_102 2002-12-04 00:00:00

NoCOUG

Brian Hitchcock November 13, 2003 Page 53

SQL Used--> create table to hold same data but in high_value order...

create table BRH_TABLE_part_data

(table_name VARCHAR(30),

tab_tblspc_name VARCHAR2(25),

tab_part_name VARCHAR2(25),

tab_high_value_text VARCHAR2(35));

--> populate the new table...

insert into BRH_TABLE_part_data

(select * from BRH_TABLE_part_data_t) order by tab_high_value_text;

select * from BRH_TABLE_part_data;

commit;

repeat for the other 2 partitioned tables...

NoCOUG

Brian Hitchcock November 13, 2003 Page 54

SQL UsedNow, for the index partitions, for each of 11 indexes, generate file of index partitions for BRH_INDEX...

spool BRH_INDEX_partition_data_05252003.txt

set long 30

set pagesize 1500

set linesize 150

set echo off

set feedback off

set heading off

select SUBSTR(index_name, 1, 25), ',', SUBSTR(tablespace_name,1,20), ',', SUBSTR(partition_name,1,20),',"', high_value, '"' from dba_ind_partitions where index_name='X_BRH_INDEX' order by partition_name;

spool off

example data:

X_BRH_INDEX, BRH_IND_TBLSPC , BRH_IND_PART_246 ," TO_DATE(' 2003-05-04 00:00:00' "

X_BRH_INDEX, BRH_IND_TBLSPC , BRH_IND_PART_10 ," TO_DATE(' 2003-05-13 00:00:00' "

X_BRH_INDEX, BRH_IND_TBLSPC , BRH_IND_PART_93 ," TO_DATE(' 2002-12-02 00:00:00' "

X_BRH_INDEX, BRH_IND_TBLSPC , BRH_IND_PART_94 ," TO_DATE(' 2002-12-03 00:00:00' "

X_BRH_INDEX, BRH_IND_TBLSPC , BRH_IND_PART_95 ," TO_DATE(' 2002-12-04 00:00:00' "

note how these partition numbers do not line up with the table partition numbers, and, note that these 5 rows were spread all over the spool file making it very hard to see which table/index partition names didn't have the same partition numbers...

NoCOUG

Brian Hitchcock November 13, 2003 Page 55

SQL Used--> create table to load this data into...

create table BRH_INDEX_part_data_t

(index_name VARCHAR(30), ind_tblspc_name VARCHAR2(25), ind_part_name VARCHAR2(25), ind_high_value_text VARCHAR2(35));

--> sqlldr control file to load this data...

sqlldr_load_BRH_INDEX_partition_data.ctl

load data

infile BRH_INDEX _partition_data_05252003.txt'

into table BRH_INDEX _part_data_t

fields terminated by ',' optionally enclosed by '"'

(index_name, ind_tblspc_name, ind_part_name, ind_high_value_text)

--> sqlldr command...

sqlldr userid=psycho/psycho control=sqlldr_load_ BRH_INDEX _partition_data.ctl log=sqlldr_load_ BRH_INDEX _partition_data.log

NoCOUG

Brian Hitchcock November 13, 2003 Page 56

SQL Used--> trim the high_value_text

update BRH_INDEX_part_data_t set ind_high_value_text=SUBSTR(ind_high_value_text,12,19);

commit;

--> create table to hold same data but in high_value order...

create table BRH_INDEX_part_data

(index_name VARCHAR(30),

ind_tblspc_name VARCHAR2(25),

ind_part_name VARCHAR2(25),

ind_high_value_text VARCHAR2(35));

NoCOUG

Brian Hitchcock November 13, 2003 Page 57

SQL Used--> populate the new table...

insert into BRH_INDEX_part_data

(select * from BRH_INDEX_part_data_t) order by ind_high_value_text;

select * from BRH_INDEX_part_data;

example data:

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND _PART_15 2002-09-15 00:00:00

X_BRH_INDEX BRH_IND _TBLSPC BRH_IND _PART_16 2002-09-16 00:00:00

X_BRH_INDEX BRH_IND _TBLSPC BRH_IND _PART_17 2002-09-17 00:00:00

X_BRH_INDEX BRH_IND _TBLSPC BRH_IND _PART_18 2002-09-18 00:00:00

X_BRH_INDEX BRH_IND _TBLSPC BRH_IND _PART_19 2002-09-19 00:00:00

commit;

repeat for the other 10 indexes

NoCOUG

Brian Hitchcock November 13, 2003 Page 58

SQL Usedwe now have partition info for all 3 tables, 11 indexes in separate tables, all ordered by high_value -- still very difficult to see which partition names don't line up between tables and indexes since they are all in separate tables

INDEX_NAME IND_TBLSPC_NAME IND_PART_NAME IND_HIGH_VALUE_TEXT

------------------------------ ------------------------- ------------------------- -----------------------------------

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_15 2002-09-15 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_16 2002-09-16 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_17 2002-09-17 00:00:00

...

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_250 2003-05-08 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_251 2003-05-09 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART _252 2003-05-10 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_8 2003-05-11 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_9 2003-05-12 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_10 2003-05-13 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_11 2003-05-14 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_12 2003-05-15 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_13 2003-05-16 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_14 2003-05-17 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_15 2003-05-18 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_16 2003-05-19 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_17 2003-05-20 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_18 2003-05-21 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_19 2003-05-22 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_20 2003-05-23 00:00:00

X_BRH_INDEX BRH_IND_TBLSPC BRH_TAB_PART_21 2003-05-24 00:00:00

NoCOUG

Brian Hitchcock November 13, 2003 Page 59

SQL Used For this index

– Partition names progress <index name>_15 to 252 <index name>_8 to 14 <table name>_15 to 21

– When ordered by high_value– the 15 to 21 are the partition names messed up

by the vendor script... other indexes are <index name>_1 to 252

– table for these indexes has partition names running 22-252, 1-21

NoCOUG

Brian Hitchcock November 13, 2003 Page 60

SQL Usedfor each index, create table that will contain the table partition names and the index partition names

create table BRH_INDEX_TI_PART_NAMES

(table_name 2 VARCHAR2(30),

tab_tblspc_name VARCHAR2(25),

tab_part_name VARCHAR2(25),

tab_high_value_text VARCHAR2(35),

index_name VARCHAR2(30),

ind_tblspc_name VARCHAR2(25),

ind_part_name VARCHAR2(25),

ind_high_value_text VARCHAR2(35),

temp_ind_part_name VARCHAR2(25),

final_ind_part_name VARCHAR2(25));

--> insert table partition name info

insert into BRH_INDEX_TI_PART_NAMES (table_name, tab_tblspc_name, tab_part_name, tab_high_value_text)

(select * from BRH_TABLE_part_data) order by tab_high_value_text;

--> insert index partition name info

insert into BRH_INDEX_TI_PART_NAMES (index_name, ind_tblspc_name, ind_part_name, ind_high_value_text)

(select * from BRH_INDEX_part_data) order by ind_high_value_text;

--> DUH!!! this creates 252 rows of table partition data followed by 252 rows of index partition data...

NoCOUG

Brian Hitchcock November 13, 2003 Page 61

SQL Usedtry again

--> insert table partition name info

insert into BRH_INDEX_TI_PART_NAMES (table_name, tab_tblspc_name, tab_part_name, tab_high_value_text)

(select * from BRH_TABLE_part_data) order by tab_high_value_text;

update BRH_INDEX_TI_PART_NAMES a

set (index_name, ind_tblspc_name, ind_part_name, ind_high_value_text) =

(select index_name , ind_tblspc_name, ind_part_name, ind_high_value_text

from BRH_INDEX_part_data b

where a.tab_high_value_text=b.ind_high_value_text);

NoCOUG

Brian Hitchcock November 13, 2003 Page 62

SQL UsedNow we can look at table partition names and index partition names at the same time

selected from BRH_INDEX_TI_PART_NAMES;

TAB_PART_NAME TAB_HIGH_VALUE_TEXT IND_PART_NAME IND_HIGH_VALUE_TEXT

------------------------------ ------------------------- ------------------------- -----------------------------------

BRH_TAB_PART_22 2002-09-15 00:00:00 BRH_IND_PART_15 2002-09-15 00:00:00

BRH_TAB_PART_23 2002-09-16 00:00:00 BRH_IND_PART_16 2002-09-16 00:00:00

BRH_TAB_PART_24 2002-09-17 00:00:00 BRH_IND_PART_17 2002-09-17 00:00:00

BRH_TAB_PART_25 2002-09-18 00:00:00 BRH_IND_PART_18 2002-09-18 00:00:00

BRH_TAB_PART_26 2002-09-19 00:00:00 BRH_IND_PART_19 2002-09-19 00:00:00

...

BRH_TAB_PART_5 2003-05-08 00:00:00 BRH_IND_PART_250 2003-05-08 00:00:00

BRH_TAB_PART_7 2003-05-10 00:00:00 BRH_IND_PART_252 2003-05-10 00:00:00

BRH_TAB_PART_9 2003-05-12 00:00:00 BRH_IND_PART_9 2003-05-12 00:00:00

BRH_TAB_PART_11 2003-05-14 00:00:00 BRH_IND_PART_11 2003-05-14 00:00:00

BRH_TAB_PART_13 2003-05-16 00:00:00 BRH_IND_PART_13 2003-05-16 00:00:00

BRH_TAB_PART_15 2003-05-18 00:00:00 BRH_TAB_PART_15 2003-05-18 00:00:00

BRH_TAB_PART_17 2003-05-20 00:00:00 BRH_TAB_PART_17 2003-05-20 00:00:00

...

BRH_TAB_PART_10 2003-05-13 00:00:00 BRH_IND_PART_10 2003-05-13 00:00:00

BRH_TAB_PART_12 2003-05-15 00:00:00 BRH_IND_PART_12 2003-05-15 00:00:00

BRH_TAB_PART_14 2003-05-17 00:00:00 BRH_IND_PART_14 2003-05-17 00:00:00

BRH_TAB_PART_16 2003-05-19 00:00:00 BRH_TAB_PART_16 2003-05-19 00:00:00

BRH_TAB_PART_18 2003-05-21 00:00:00 BRH_TAB_PART_18 2003-05-21 00:00:00

BRH_TAB_PART_20 2003-05-23 00:00:00 BRH_TAB_PART_20 2003-05-23 00:00:00

NoCOUG

Brian Hitchcock November 13, 2003 Page 63

SQL UsedNote the additional columns TEMP_IND_PART_NAME, FINAL_IND_PART_NAME these are going to hold the two new names for each partition

some of the index partitions already have the vendor specified naming convention, if we simply changed all existing index partition names to the vendor convention, we would

get errors since, for some partitions, the partition name is already in use...

update BRH_INDEX_TI_PART_NAMES set temp_ind_part_name=TRIM(ind_part_name)||'_temp';

be careful to order by high_value...

SQL> select * from BRH_INDEX_TI_PART_NAMES order by tab_high_value_text;

TABLE_NAME TAB_TBLSPC_NAME TAB_PART_NAME TAB_HIGH_VALUE_TEXT INDEX_NAME IND_TBLSPC_NAME IND_PART_NAME

IND_HIGH_VALUE_TEXT TEMP_IND_PART_NAME FINAL_IND_PART_NAME

------------------------------ ------------------------- ------------------------- ----------------------------------- ------------------------------ ------------------------- ------------------------- ----------------------------------- ------------------------- -------------------------

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_22 2002-09-15 00:00:00 X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_15 2002-09-15 00:00:00

BRH_IND_PART_15_temp

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_23 2002-09-16 00:00:00 X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_16 2002-09-16 00:00:00

BRH_IND_PART_16_temp

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_24 2002-09-17 00:00:00 X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_17 2002-09-17 00:00:00

BRH_IND_PART_17_temp

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_25 2002-09-18 00:00:00 X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_18 2002-09-18 00:00:00

BRH_IND_PART_18_temp

BRH_TABLE BRH_TAB_TBLSPC BRH_TAB_PART_26 2002-09-19 00:00:00 X_BRH_INDEX BRH_IND_TBLSPC BRH_IND_PART_19 2002-09-19 00:00:00

BRH_IND_PART_19_temp

NoCOUG

Brian Hitchcock November 13, 2003 Page 64

SQL Usedupdate BRH_INDEX_TI_PART_NAMES set temp_ind_part_name=TRIM(ind_part_name)||'_temp';

fix index partition names that were messed up by vendor script

update BRH_INDEX_TI_PART_NAMES set temp_ind_part_name='BRH_INtmp'||SUBSTR(temp_ind_part_name,12,25)

where temp_ind_part_name like 'BRH_TAB_PART%';

example

BRH_TAB_PART_15_temp becomes BRH_INtmp_15_temp

--> start building final index partition name, start with the table partition name...

this gives us the number for the final index partition name, i.e. the same as the table partition name number

update BRH_INDEX_TI_PART_NAMES set final_ind_part_name=tab_part_name;

this replaces the table name with the vendor required spec for the index partition name

update BRH_INDEX_TI_PART_NAMES set final_ind_part_name='BRH_IND_PART'||SUBSTR(final_ind_part_name,12,25);

NoCOUG

Brian Hitchcock November 13, 2003 Page 65

SQL UsedFinally, we have all the data we need to construct the SQL statements to alter all the index partition names

First, rename all the index partition names to the temporary index partition names to deal with duplicate conflicts

SQL> select 'alter index analysis.'||TRIM(index_name)||' rename partition '||TRIM(ind_part_name)||' TO '||TRIM(temp_ind_part_name)||';‘ from BRH_INDEX_TI_PART_NAMES order by tab_high_value_text;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_15 TO BRH_IND_PART_15_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_16 TO BRH_IND_PART_16_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_17 TO BRH_IND_PART_17_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_18 TO BRH_IND_PART_18_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_19 TO BRH_IND_PART_19_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_20 TO BRH_IND_PART_20_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_21 TO BRH_IND_PART_21_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_22 TO BRH_IND_PART_22_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_23 TO BRH_IND_PART_23_temp;

...

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_13 TO BRH_IND_PART_13_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_14 TO BRH_IND_PART_14_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_15 TO BRH_INtmp_15_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_16 TO BRH_INtmp_16_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_17 TO BRH_INtmp_17_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_18 TO BRH_INtmp_18_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_19 TO BRH_INtmp_19_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_20 TO BRH_INtmp_20_temp;

alter index analysis.X_BRH_INDEX rename partition BRH_TAB_PART_21 TO BRH_INtmp_21_temp;

NoCOUG

Brian Hitchcock November 13, 2003 Page 66

SQL UsedSecond, rename index partition names to their final names using the same number(s) as the table partition numbers

select 'alter index analysis.'||TRIM(index_name)||' rename partition '||TRIM(temp_ind_part_name)||' TO '||TRIM(final_ind_part_name)||';‘ from BRH_INDEX_TI_PART_NAMES order by tab_high_value_text;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_15_temp TO BRH_IND_PART_22;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_16_temp TO BRH_IND_PART_23;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_17_temp TO BRH_IND_PART_24;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_18_temp TO BRH_IND_PART_25;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_19_temp TO BRH_IND_PART_26;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_20_temp TO BRH_IND_PART_27;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_21_temp TO BRH_IND_PART_28;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_22_temp TO BRH_IND_PART_29;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_23_temp TO BRH_IND_PART_30;

...

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_13_temp TO BRH_IND_PART_13;

alter index analysis.X_BRH_INDEX rename partition BRH_IND_PART_14_temp TO BRH_IND_PART_14;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_15_temp TO BRH_IND_PART_15;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_16_temp TO BRH_IND_PART_16;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_17_temp TO BRH_IND_PART_17;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_18_temp TO BRH_IND_PART_18;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_19_temp TO BRH_IND_PART_19;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_20_temp TO BRH_IND_PART_20;

alter index analysis.X_BRH_INDEX rename partition BRH_INtmp_21_temp TO BRH_IND_PART_21;

NoCOUG

Brian Hitchcock November 13, 2003 Page 67

SQL Usedthe SQL is spooled to separate files and executed against the db

spool BRH_INDEX_TI_PART_NAMES_tmp_SQL.txt

select 'alter index analysis.'||TRIM(index_name)||' rename partition '||TRIM(ind_part_name)||' TO '||TRIM(temp_ind_part_name)||';'

from BRH_INDEX_TI_PART_NAMES order by tab_high_value_text;

spool off

spool BRH_INDEX_TI_PART_NAMES_final_SQL.txt

select 'alter index analysis.'||TRIM(index_name)||' rename partition '||TRIM(temp_ind_part_name)||' TO '||TRIM(final_ind_part_name)||';'

from BRH_INDEX_TI_PART_NAMES order by tab_high_value_text;

spool off

--> repeat for the other 10 indexes (11 indexes total)

NoCOUG

Brian Hitchcock November 13, 2003 Page 68

Script Testing

Use development environment Subset of production data

– Same number of daily partitions as production– Rename existing index partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 69

Result?

Scripts ran without error Everyone is happy Prepared to do it all again if needed No need for cut/paste Demonstrated method to handle high_value

– LONG datatype work-around

NoCOUG

Brian Hitchcock November 13, 2003 Page 70

Performance Issues

After all this– Are you sure you are getting the benefits?

Explain plan shows partitions accessed Partition Pruning

– Optimizer can tell which partitions aren’t involved in completing the query

Need to check– Partition pruning is happening– Where clause can prevent this

User generated SQL is problematic

NoCOUG

Brian Hitchcock November 13, 2003 Page 71

Explain Plan -- Goodexplain plan set Statement_Id = 'TEST' for

CREATE TABLE BRHPSYCHO1 NOLOGGING

AS SELECT t161.brh_col1 AS col0, t168.brh_col2 AS col1, t172.brh_col2 AS col2,

t176.brh_col2 AS col3,

COUNT(DISTINCT t161.brh_col3) AS countcol

FROM brh_table1 t161, brh_table2 t168, brh_table2 t172, brh_table2 t176, brh_table2 t185

WHERE t172.brh_col4 = t176.brh_col3 AND t168.brh_col4 = t172.brh_col3 AND

t161.brh_col5 = t185.brh_col3 AND t185.brh_col4 = t168.brh_col3 AND

t161.brh_col6 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t161.brh_col6 <= TO_DATE('2001/08/0823:59:59','YYYY/MM/DDHH24:MI:SS') AND

t185.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t185.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DDHH24:MI:SS') AND

t168.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t168.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') AND

t172.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t172.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') AND

t176.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t176.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DDHH24:MI:SS')

GROUP BY t161.brh_col1, t168.brh_col2, t172.brh_col2, t176.brh_col2;

NoCOUG

Brian Hitchcock November 13, 2003 Page 72

Explain Plan -- Good--------------------------------------------------------------------------------

| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop

--------------------------------------------------------------------------------

| CREATE TABLE STATEMENT | | 4 | 448 | 67 | | |

| LOAD AS SELECT | | | | | | |

| SORT GROUP BY | | 4 | 448 | 67 | | |

| NESTED LOOPS | | 4 | 448 | 55 | | |

| NESTED LOOPS | | 4 | 372 | 43 | | |

| NESTED LOOPS | | 4 | 272 | 31 | | |

| NESTED LOOPS | | 4 | 172 | 19 | | |

| TABLE ACCESS BY LOC|brh_table1| 4 | 92 | 7 | 1 | 1 |

| INDEX RANGE SCAN |brh_ind1 | 4 | | 3 | 1 | 1 |

--------------------------------------------------------------------------------

| TABLE ACCESS BY LOC|brh_table2| 28 | 560 | 3 | 2 | 2 |

| INDEX RANGE SCAN |brh_ind2 | 28 | | 2 | 2 | 2 |

| TABLE ACCESS BY LOCA|brh_table2| 28 | 700 | 3 | 2 | 2 |

| INDEX RANGE SCAN |brh_ind2 | 28 | | 2 | 2 | 2 |

| TABLE ACCESS BY LOC |brh_table2| 28 | 700 | 3 | 2 | 2 |

| INDEX RANGE SCAN |brh_ind2 | 28 | | 2 | 2 | 2 |

| TABLE ACCESS BY LOCAL |brh_table2| 28 | 532 | 3 | 2 | 2 |

| INDEX RANGE SCAN |brh_ind2 | 28 | | 2 | 2 | 2 |

--------------------------------------------------------------------------------

$ORACLE_HOME/rdbms/admin/utlxpls (use correct version!)

NoCOUG

Brian Hitchcock November 13, 2003 Page 73

Explain Plan -- Badexplain plan set Statement_Id = 'TEST' for

CREATE TABLE BRHPSYCHO1 NOLOGGING

AS SELECT t161.brh_col1 AS col0, t168.brh_col2 AS col1, t172.brh_col2 AS col2,

t176.brh_col2 AS col3,

COUNT(DISTINCT t161.brh_col3) AS countcol

FROM brh_table1 t161, brh_table2 t168, brh_table2 t172, brh_table2 t176,

brh_table2 t185

WHERE t172.brh_col4 = t176.brh_col3 AND t168.brh_col4 = t172.brh_col3 AND

t161.brh_col5 = t185.brh_col3 AND t185.brh_col4 = t168.brh_col3 AND

t161.brh_col6 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t161.brh_col6 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') AND

t185.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') OR t185.brh_col3=0 AND

t185.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') OR t185.brh_col3=0 AND

t168.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') OR t168.brh_col3=0 AND

t168.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') OR t168.brh_col3=0 AND

t172.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') OR t172.brh_col3=0 AND

t172.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DD HH24:MI:SS') OR t172.brh_col3=0 AND

t176.brh_col7 >= TO_DATE('2001/08/08 00:00:00','YYYY/MM/DD HH24:MI:SS') AND

t176.brh_col7 <= TO_DATE('2001/08/08 23:59:59','YYYY/MM/DDHH24:MI:SS')

GROUP BY t161.brh_col1, t168.brh_col2, t172.brh_col2, t176.brh_col2;

NoCOUG

Brian Hitchcock November 13, 2003 Page 74

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |

--------------------------------------------------------------------------------

| CREATE TABLE STATEMENT | |4254322|443760|70970810| | |

| LOAD AS SELECT | | | | | | |

| SORT GROUP BY | |4254322|443760| | | |

| SORT GROUP BY | |4254322|443760| | | |

| SORT GROUP BY | |4254322|443760| | | |

| CONCATENATION | | | | | | |

| MERGE JOIN CARTESIAN| | 4 | 172 | 83 | | |

| MERGE JOIN CARTESIA| | 4 | 272 | 167 | | |

| MERGE JOIN CARTESI| | 4 | 92 | | | |

Plan Table --------------------------------------------------------------------------------

| MERGE JOIN CARTES| | 4 | 448 | 335 | | |

| PARTITION RANGE | | | | | 1 | 10 |

| TABLE ACCESS BY|brh_table2| 4M| 115M| 21 | 1 | 10 |

| INDEX RANGE SC|brh_ind2 | 4M| | 20 | 1 | 10 |

| SORT JOIN | | 4M| 87M| | | |

| TABLE ACCESS BY|brh_table2| 4M| 87M| 21 | 2 | 2 |

| INDEX RANGE SC|brh_ind3 | 4M| | 20 | 2 | 2 |

| SORT JOIN| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 9 |

| TABLE ACCESS FU|brh_table1| 4 | 92 | 7 | 1 | 9 |

| SORT JOIN | | 4M| 115M| | | |

NoCOUG

Brian Hitchcock November 13, 2003 Page 75

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 115M| 21 | 1 | 10 |

| SORT JOIN | | 4M| 92M| | | |

| PARTITION RANGE AL| | | | | 1 | 10 |

| TABLE ACCESS FULL|brh_table2| 4M| 92M| 19 | 1 | 10 |

| MERGE JOIN CARTESIAN| | 4 | 172 | 83 | | |

| MERGE JOIN CARTESIA| | 4 | 272 | 167 | | |

| NESTED LOOPS| | 4 | 448 | 335 | | |

| MERGE JOIN CARTES| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 2 |

| TABLE ACCESS BY|brh_table2| 4M| 115M| 21 | 1 | 2 |

Plan Table --------------------------------------------------------------------------------

| INDEX RANGE SC|brh_ind2 | 4M| | 20 | 1 | 2 |

| SORT JOIN| | 4 | 92 | | | |

| PARTITION RANGE| | | | | 1 | 9 |

| TABLE ACCESS F|brh_table1| 4 | 92 | 7 | 1 | 9 |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 87M| 21 | 1 | 10 |

| SORT JOIN | | 4M| 115M| | | |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 115M| 21 | 1 | 10 |

| SORT JOIN | | 4M| 92M| | | |

| PARTITION RANGE AL| | | | | 1 | 10 |

NoCOUG

Brian Hitchcock November 13, 2003 Page 76

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| TABLE ACCESS FULL|brh_table2| 4M| 92M| 19 | 1 | 10 |

| NESTED LOOPS| | 4 | 372 | 251 | | |

| MERGE JOIN CARTESIA| | 4 | 172 | 83 | | |

| MERGE JOIN CARTESI| | 4 | 448 | 335 | | |

| MERGE JOIN CARTES| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 10 |

| TABLE ACCESS BY|brh_table2| 4M| 115M| 21 | 1 | 10 |

| INDEX RANGE SC|brh_ind2 | 4M| | 20 | 1 | 10 |

| SORT JOIN| | 4 | 92 | | | |

| PARTITION RANGE| | | | | 1 | 9 |

| TABLE ACCESS F|brh_table1| 4 | 92 | 7 | 1 | 9 |

Plan Table --------------------------------------------------------------------------------

| SORT JOIN| | 4M| 87M| | | |

| PARTITION RANGE | | | | | 1 | 10 |

| TABLE ACCESS FU|brh_table2| 4M| 87M| 21 | 1 | 10 |

| SORT JOIN | | 4M| 92M| | | |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 92M| 19 | 1 | 10 |

| PARTITION RANGE ITE| | | | | 2 | 10 |

| TABLE ACCESS FULL |brh_table2| 4M| 115M| 21 | 2 | 10 |

| MERGE JOIN CARTESIAN| | 4 | 172 | 83 | | |

| NESTED LOOPS| | 4 | 448 | 335 | | |

| NESTED LOOPS| | 4 | 372 | 251 | | |

NoCOUG

Brian Hitchcock November 13, 2003 Page 77

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| MERGE JOIN CARTES| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 2 |

| TABLE ACCESS BY|brh_table2| 4M| 115M| 21 | 1 | 2 |

| INDEX RANGE SC|brh_ind2 | 4M| | 20 | 1 | 2 |

| SORT JOIN| | 4 | 92 | | | |

| PARTITION RANGE| | | | | 1 | 9 |

| TABLE ACCESS F|brh_table1| 4 | 92 | 7 | 1 | 9 |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 115M| 21 | 1 | 10 |

| PARTITION RANGE AL| | | | | 1 | 10 |

| TABLE ACCESS FULL|brh_table2| 4M| 87M| 21 | 1 | 10 |

Plan Table --------------------------------------------------------------------------------

| SORT JOIN | | 4M| 92M| | | |

| PARTITION RANGE AL| | | | | 1 | 10 |

| TABLE ACCESS FULL|brh_table2| 4M| 92M| 19 | 1 | 10 |

| NESTED LOOPS| | 4 | 272 | 167 | | |

| NESTED LOOP| | 4 | 448 | 335 | | |

| MERGE JOIN CARTESI| | 4 | 372 | 251 | | |

| MERGE JOIN CARTES| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 10 |

| TABLE ACCESS BY|brh_table2| 4M| 92M| 19 | 1 | 10 |

| INDEX RANGE SC|brh_ind2 | 4M| | 18 | 1 | 10 |

| SORT JOIN| | 4 | 92 | | | |

NoCOUG

Brian Hitchcock November 13, 2003 Page 78

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| PARTITION RANGE| | | | | 1 | 9 |

| TABLE ACCESS F|brh_table1| 4 | 92 | 7 | 1 | 9 |

| SORT JOIN| | 4M| 115M| | | |

| PARTITION RANGE | | | | | 1 | 10 |

| TABLE ACCESS FU|brh_table2| 4M| 115M| 21 | 1 | 10 |

| PARTITION RANGE AL| | | | | 1 | 10 |

| TABLE ACCESS FULL|brh_table2| 4M| 87M| 21 | 1 | 10 |

| PARTITION RANGE ITE| | | | | 2 | 10 |

| TABLE ACCESS FULL |brh_table2| 4M| 115M| 21 | 2 | 10 |

| NESTED LOOPS| | 4 | 448 | 335 | | |

| NESTED LOOP| | 4 | 372 | 251 | | |

Plan Table --------------------------------------------------------------------------------

| NESTED LOOPS| | 4 | 272 | 167 | | |

| MERGE JOIN CARTES| | 4 | 92 | | | |

| PARTITION RANGE | | | | | 1 | 2 |

| TABLE ACCESS BY|brh_table2| 4M| 92M| 19 | 1 | 2 |

| INDEX RANGE SC|brh_ind2 | 4M| | 18 | 1 | 2 |

| SORT JOIN| | 4 | 92 | | | |

| PARTITION RANGE| | | | | 1 | 9 |

| TABLE ACCESS F|brh_table1| 4 | 92 | 7 | 1 | 9 |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS FUL|brh_table2| 4M| 115M| 21 | 1 | 10 |

| PARTITION RANGE AL| | | | | 1 | 10 |

NoCOUG

Brian Hitchcock November 13, 2003 Page 79

Explain Plan -- BadPlan Table --------------------------------------------------------------------------------

| TABLE ACCESS FULL|brh_table2| 4M| 115M| 21 | 1 | 10 |

| PARTITION RANGE ALL| | | | | 1 | 10 |

| TABLE ACCESS FULL |brh_table2| 4M| 87M| 21 | 1 | 10 |

| NESTED LOOPS| | 4 | 448 | 335 | | |

| NESTED LOOPS| | 4 | 372 | 251 | | |

| NESTED LOOPS| | 4 | 272 | 167 | | |

| NESTED LOOPS | | 4 | 172 | 83 | | |

| TABLE ACCESS BY |brh_table1| 4 | 92 | 7 | 1 | 1 |

| INDEX RANGE SCA|brh_ind1 | 4 | | 3 | 1 | 1 |

| PARTITION RANGE | | | | | 2 | 10 |

| TABLE ACCESS BY|brh_table2| 4M| 92M| 19 | 2 | 10 |

Plan Table --------------------------------------------------------------------------------

| INDEX RANGE SC|brh_ind2 | 4M| | 18 | 2 | 10 |

| PARTITION RANGE A| | | | | 1 | 10 |

| TABLE ACCESS BY |brh_table2| 4M| 115M| 21 | 1 | 10 |

| INDEX RANGE SCA|brh_ind2 | 4M| | 20 | 1 | 10 |

| PARTITION RANGE AL| | | | | 1 | 10 |

| TABLE ACCESS BY L|brh_table2| 4M| 115M| 21 | 1 | 10 |

| INDEX RANGE SCAN|brh_ind2 | 4M| | 20 | 1 | 10 |

| PARTITION RANGE ALL| | | | | 1 | 10 |

| TABLE ACCESS BY LO|brh_table2| 4M| 87M| 21 | 1 | 10 |

| INDEX RANGE SCAN |brh_ind2 | 4M| | 20 | 1 | 10 |

--------------------------------------------------------------------------------

132 rows selected.

NoCOUG

Brian Hitchcock November 13, 2003 Page 80

Why Bad?

Optimizer can’t predict the future– Bind variables

Don’t know range of values needed until run time

– Join order Can’t see which partitions needed until too late

Other reasons– Old statistics

Distribution of rows among partitions

SQL may address all partitions

NoCOUG

Brian Hitchcock November 13, 2003 Page 81

How To Choose Partition Size?

For queries– partition size may not matter– SQL must be written specifically to take advantage of

partitions (pruning)– Do most queries look at a continuous range of days,

weeks? For Loading data

– How often do you load data?– Good to analyze small amounts of data

For backups– Once loaded, partitions in dedicated tablespace can be

read-only, reduce size of backups

NoCOUG

Brian Hitchcock November 13, 2003 Page 82

Partitioned Tables

Good– Improve performance– Improve management

Load, analyze

Bad– Increased administration– Confusing (3528 partitions to deal with)– Only specific SQL will perform well– Which tables to partition?