Oracle 11g Reference Partitioning and Indexes - oracle

I have a parent table containing the following columns:
- PARENT_ID: UUID
- EVENT_DATE: TIMESTAMP
- DATA_COLUMN1: VARCHAR2(255)
- DATA_COLUMN2: VARCHAR2(255)
The table is range partitioned by EVENT_DATE. Data is only retained for a month and the last partition is dropped on a daily basis.
Following my understanding, using a global index for PK would result in sub-standard performance when dropping a partition. This means that the PK of this table has to be based on both PARENT_ID + EVENT_DATE in order to create a local index.
I have a second table that is the child of the first (via one-to-many relationship). It has the following columns:
- CHILD_ID: UUID
- PARENT_ID: UUID - FK into parent table
- DATA_COLUMN3: VARCHAR2(255)
- DATA_COLUMN4: VARCHAR2(255)
To partition the child table, I decided to use reference partitioning. One of its big advantages: it removes the need to duplicate the partition key in the child table. However, based on my reasoning, the only way to achieve this is through global indexes. Here is my train of thought:
For the unique index of the parent table to be local, the PK must include the partition key, e.g. EVENT_DATE.
A foreign key constrain cannot reference only part of the PK. Thus the child table must include both PARENT_ID and EVENT_DATE columns.
What's more, I also read that "When using reference partitioning, most child table indexes
should be defined as global, unless there is a compelling reason
for a given index to be defined as local." (http://www.nocoug.org/download/2010-05/Zitelli-Reference_Partitioning_NoCOUG.pdf).
Am I missing something or is there no way to use reference partitioning without having global indexes or duplicating data?
An explanation on how reference partitioning works with local/global indexes will be much appreciated!

You understand correctly. if you want to create a reference partition you need to define a valid FK. in your case - both parent_id and event_id needs to be present in the child table.
Ref partitions are for cases when you want to partition a table according to a column not in the PK those not in the child table. this is not your case - you can apply range partition on both tables and gain max pruning.
The event_date in the child table is not redundant - it's required by the model - you need data_columns 3/4 in the child table for each instance of parent_id + event_date.
Regarding the local indexes on a child table in a ref partitioned table - my logic say exactly the opposite. if i have a ref partition i'm aiming at max pruning which means that i want as less partitions as possible to be accessed in each query. in this case i would want local indexes and not global.
You said "using a global index for PK would result in sub-standard performance when dropping a partition". when you drop a partition from the table all global indexes will be invalidated and you will have to rebuild them. this is the only performance impact regarding DDL changes. PK on a partitioned table must be global so you don't have a choice here any way.

Though above answer has been answered but on you thought: 'A foreign key constrain cannot reference only part of the PK. Thus the child table must include both PARENT_ID and EVENT_DATE columns.'
I believe FK can refer to part of PK if your attribute has been defined with Unique constraint & I see that possible in your example.
Example:
Table A ( orderid, orderdate, custid)
PF -> OrderID, OrderDate
Table B(OrderID, ItemID)
In table B, orderID can be FK if OrderID is defined as Unique in Table A.
I hope this helps.

Related

Oracle Reference Partition - How to make sure I'm getting the benefits when I select

I'm trying to make sure I'm getting the benefit of selecting from a partition when using reference partitions.
In normal partitions, I know you have to include the column(s) on which the partition is defined in order for Oracle to know it can just search one specific partition.
My question is, when I'm selecting from a reference-partitioned table, do I just need to include the column on which the reference foreign key is defined? Or do I need to join and include the parent table's column on which the partition is actually defined?
create table alpha (
name varchar2(240) not null,
partition_no number(14) not null,
constraint alpha_pk
primary key (name),
constraint alpha_c01
check (partition_no > 0)
)
partition by range(partition_no)
interval (1)
(partition empty values less than (1))
;
create table beta (
name varchar2(240) not null,
alpha_name varchar2(240) not null,
some_data number not null,
constraint beta_pk
primary key (name),
constraint beta_f01
foreign key (alpha_name)
references alpha (name)
)
partition by reference (beta_f01)
;
Assume the tables in production will have much more data in them, with hundreds of millions of rows in the beta table, but merely thousands per partition.
Is this all I need?
select b.some_data
from beta b
where b.alpha_name = 'Blah'
;
Thanks if anyone can verify this for me. Or can explain anything else I'm missing with regard to properly creating indexes in reference-partitioned tables.
[Edit] Removed part of the example where clause that shouldn't have been there. The example is meant to represent reading the reference-partitioned with just the reference partition foreign key in the where clause.

Move an Oracle table to being Index Organised

I have an Oracle table in a live production environment and the table is over half a gig in size. Is it possible to change this normal Oracle table from being heap organised to index organised or is this only achievable by moving the data from this table to another new table which is index organised? Either way, I would be grateful if you could you please list the steps involved in this procedure.
There is no way to alter a table to make it index-organized table. Instead you can redefine the table(using DBMS_REDEFINITION)or can create new table using CTAS.
Example:
create table t2 (
id number, first_name varchar2(20),
constraint pk_id primary key (id)
)
organization index
as select * from t1;
I never used DBMS_REDEFINITION but with CTAS it is not only step to create table if it is production.
List all indexes, constraints, foreign keys and so on based on system views
Prepare create index, constraints and alter foreign keys statements. prepare list of triggers, procedures that depend on table.
Create table as select (consider lock before that step if you can)
Create all indexes, constraints with prepared statements from step 2
Swap table names and swap foreign keys (this step may cause some errors if you hit insert on foreign keys (if you expect it on that time you should lock the table and tables referencing by foreign key).
Compile dependent objects from 2 (if you locked tables unlock here)
(if you haven't locked on step 3) Insert into table select * from new minus select * from old; or if you have timstamp of inserting row just insert new rows.
I hope the list is complete.

List partitioning in oracle

I have a table with following schema:
MyTable {
User_ID,
Task_ID,
Task_Description
}
where Task_ID is the primary key.
I wish to partition it on User_ID. Also, new users may be added and I want new corresponding partitions to get created automatically.
I went through this (see page-8) and found that Oracle 11g provides Interval partitioning which does similar thing but with intervals.
Can I do the same with User_ID?
You can't automatically generate a unique partition for each distinct varchar(128) value.
You could hash partition the table. That would not guarantee that every partition had a single, unique user_id value. It would ensure that all the rows with the same user_id were in a single partition and would eliminate the need to do manual partition maintenance.
You could list partition the table. That would require, though, that you explicitly add a new partition when a new user_id value is added.
If the user_id values were strictly predictable, you could probably do something with an interval partitioning scheme on a virtual column. But that seems highly unlikely to be practical.
What is the business problem that you are trying to solve? Why is it necessary to have a single user_id value in each partition? Why are you partitioning the table in the first place?

Oracle: Index organised table with null values

I have a table which is basically a tree structure with a column parent_id and id.
parent_id is null for root nodes.
There is also a self referential foreign key, so that every parent_id has a corresponding id.
This table is mainly read-only with mostly infrequent batch updates.
One of the most common queries from the application which accesses this table is select ... where parent_id = X. I thought this might be faster if this table was index organised on parent_id.
However, I'm not sure how to index organise this table if parent_id can be null. I'd rather not fudge things so that parent_id=0 is some special id, as I'd have to add dummy values to the table to ensure the foreign key constraints are satisfied, and it also changes the application logic.
Is there any way to index organise a table by possible null value columns?
Solution from question asker:
I found I could get the same benefits from index organisation just by adding the queried columns to the end of the parent_id index, i.e. instead of:
create index foo_idx on foo_tab(parent_id);
I do:
create index foo_idx on foo_tab(parent_id, col1, col2, col3);
Where col1, col2, col3 etc are frequently accessed columns.
I've only done this with indexes which are used to return multiple rows which benefit from the ordering and hence disk locality provided by the index, instead of having to jump around the table. Indexes which are generally used to return single rows I've left to reference the table, as there is only one row to read anyway so locality matters much less.
Like I mentioned, this is a mainly read table, and also space is not a huge concern, so I don't think the overhead to writes caused by these indexes is a big concern.
(I realise this won't index null parent_ids, but instead I've made another index on decode(parent_id, null, 1, null) which indexes nulls and only nulls).
I would try adding the index on the single column parent_id.
If all of the columns in your index are non-null, then this row does not appear in your index.
So for the parent_id = X you cite above, this should use the index. However, if you're doing parent_id is null, then it won't use the index, and you'll be getting the same performance as you have now. This sounds like behaviour that would suit you.
I have used this in the past to improve the performance of queries. It works particulalry well if the number of items in the index is small compared to the number of rows in the database. We had about 3% of our rows in this particular index, and it flew :-)
But, as always, you need to try it and measure the difference in performance. Your mileage may vary.

Is an Index Organized Table appropriate here?

I recently was reading about Oracle Index Organized Tables (IOTs) but am not sure I quite understand WHEN to use them. So I have a small table:
create table categories
(
id VARCHAR2(36),
group VARCHAR2(100),
category VARCHAR2(100
)
create unique index (group, category, id) COMPRESS 2;
The id column is a foreign key from another table entries and my common query is:
select e.id, e.time, e.title from entries e, categories c where e.id=c.id AND e.group=? AND c.category=? ORDER by e.time
The entries table is indexed properly.
Both of these tables have millions (16M currently) of rows and currently this query really stinks (note: I have it wrapped in a pagination query also so I only get back the first 20, but for simplicity I omitted that).
Since I am basically indexing the entire table, does it make sense to create this table as an IOT?
EDIT by popular demand:
create table entries
(
id VARCHAR2(36),
time TIMESTAMP,
group VARCHAR2(100),
title VARCHAR2(500),
....
)
create index (group, time) compress 1;
My real question I dont think depends on this though. Basically if you have a table with few columns (3 in this example) and you are planning on putting a composite index on all three rows is there any reason not to use an IOT?
IOTs are great for a number of purposes, including this case where you're gonna have an index on all (or most) of the columns anyway - but the benefit only materialises if you don't have the extra index - the idea is that the table itself is an index, so put the columns in the order that you want the index to be in. In your case, you're accessing category by id, so it makes sense for that to be the first column. So effectively you've got an index on (id, group, category). I don't know why you'd want an additional index on (group, category, id).
Your query:
SELECT e.id, e.time, e.title
FROM entries e, categories c
WHERE e.id=c.id AND e.group=? AND c.category=?
ORDER by e.time
You're joining the tables by ID, but you have no index on entries.id - so the query is probably doing a hash or sort merge join. I wouldn't mind seeing a plan for what your system is doing now to confirm.
If you're doing a pagination query (i.e. only interested in a small number of rows) you want to get the first rows back as quick as possible; for this to happen you'll probably want a nested loop on entries, e.g.:
NESTED LOOPS
ACCESS TABLE BY ROWID - ENTRIES
INDEX RANGE SCAN - (index on ENTRIES.group,time)
ACCESS TABLE BY ROWID - CATEGORIES
INDEX RANGE SCAN - (index on CATEGORIES.ID)
Since the join to CATEGORIES is on ID, you'll want an index on ID; if you make it an IOT, and make ID the leading column, that might be sufficient.
The performance of the plan I've shown above will be dependent on how many rows match the given "group" - i.e. how selective an average "group" is.
Have you looked at dba-oracle.com, asktom.com, IOUG, another asktom.com?
There are penalties to pay for IOTs - e.g., poorer insert performance
Can you prototype it and compare performance?
Also, perhaps you might want to consider a hash cluster.
IOT's are a trade off. You are getting access performance for decreased insert/update performance. We typically use them for reference data that is batch loaded daily and not updated during the day. This is not to say it's the only way to use them, just how we use them.
Few things here:
You mention pagination - have you considered the first_rows hint?
Is that the order your index is in, with group as the first field? If so I'd consider moving ID to be the first column since that index will not be used.
foreign keys should have an index on the column. Consider addind an index on the foreign key (id column).
Are you sure it's not the ORDER BY causing slowness?
What version of Oracle are you using?
I ASSUME there is a primary key on table entries for field id, correct?
Why the WHERE condition does not include "c.group = e.group" ?
Try to:
Remove the order by condition
Change the index definition from "create unique index (group,
category, id)" to "create unique index (id, group, category)"
Reorganise table categories as an IOT on (group, category, id)
Reorganise table categories as an IOT on (id, group, category)
In each of the above case use EXPLAIN PLAN to review the cost

Resources