Hi,
I thought I understood the advantage of using an IOT but I'm still confused,
I created both the table as an IOT and a normal heap table with the following indexes:
CREATE INDEX "DOCUMENT_IDX_01" ON "DOCUMENT" ("NIU","REQUEST_NUMBER", "DOCUMENT_TYPE", "SENDER_ROOT") LOCAL;
CREATE UNIQUE INDEX "DOCUMENT_UQ_01" ON "DOCUMENT" ("NIU","TRACKING_ID", "DOCUMENT_TYPE", "ENTITY_IDENTIFIER") LOCAL;
According to the design of the application we will never query more than one NIU at a time, meaning we will always query on partition at a time, so I created local indexes.
Here is a test query:
select * from CLINICAL_DOCUMENT where NIU='10000017400' and clinical_document_id='81873';
The plan with the IOT:
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 302 | 1 (0)| 00:00:01 | | |
| 1 | PARTITION HASH SINGLE| | 1 | 302 | 1 (0)| 00:00:01 | 1 | 1 |
|* 2 | INDEX UNIQUE SCAN | DOCUMENT_PK | 1 | 302 | 1 (0)| 00:00:01 | 1 | 1 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DOCUMENT_ID"=81873 AND "NIU"='10000017400')
The plan with the heap table:
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2421 | 2 (0)| 00:00:01 | | |
|* 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| DOCUMENT | 1 | 2421 | 2 (0)| 00:00:01 | 1025 | 1025 |
|* 2 | INDEX UNIQUE SCAN | DOCUMENT_PK | 1 | | 1 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("NIU"='10000017400')
2 - access("DOCUMENT_ID"=81873)
Here is an other query using a local index previously created:
select * from DOCUMENT where niu='10000017400' and REQUEST_NUMBER='2014148000366-161751620' and SENDER_ROOT='2.16.124.10.101.1.60.1.4503' and PART_NUMBER='539013823';
The plan with the IOT:
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 302 | 2 (0)| 00:00:01 | | |
| 1 | PARTITION HASH SINGLE| | 1 | 302 | 2 (0)| 00:00:01 | 1 | 1 |
|* 2 | INDEX UNIQUE SCAN | DOCUMENT_PK | 1 | 302 | 2 (0)| 00:00:01 | 1 | 1 |
|* 3 | INDEX RANGE SCAN | DOCUMENT_IDX_01 | 1 | | 1 (0)| 00:00:01 | 1 | 1 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("REQUEST_NUMBER"='2014148000366-161751620' AND "SENDER_ROOT"='2.16.124.10.101.1.60.1.4503'
AND "NIU"='10000017400')
filter("PART_NUMBER"='539013823')
3 - access("REQUEST_NUMBER"='2014148000366-161751620' AND "SENDER_ROOT"='2.16.124.10.101.1.60.1.4503'
AND "NIU"='10000017400')
filter("NIU"='10000017400' AND "SENDER_ROOT"='2.16.124.10.101.1.60.1.4503')
The plan with the heap table:
---------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2421 | 2 (0)| 00:00:01 | | |
| 1 | PARTITION HASH SINGLE | | 1 | 2421 | 2 (0)| 00:00:01 | 1025 | 1025 |
|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| DOCUMENT | 1 | 2421 | 2 (0)| 00:00:01 | 1025 | 1025 |
|* 3 | INDEX RANGE SCAN | DOCUMENT_IDX_01 | 1 | | 1 (0)| 00:00:01 | 1025 | 1025 |
---------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("PART_NUMBER"='539013823')
3 - access("NIU"='10000017400' AND "REQUEST_NUMBER"='2014148000366-161751620' AND
"SENDER_ROOT"='2.16.124.10.101.1.60.1.4503')
filter("SENDER_ROOT"='2.16.124.10.101.1.60.1.4503')
I still wonder why it is an advantage to use an IOT, why grouping the NIU together can have a benefit for the performance?
Thanks
Cyrille
July 20, 2016 - 3:37 pm UTC
I still wonder why it is an advantage to use an IOT, why grouping the NIU together can have a benefit for the performance?
This is based on the assumption there's more than one row for each NIU.
Say you have 10 rows/NIU. In a heap table these could easily be scattered across 10 different table blocks. So fetch these that's 10 IO operations you need to do + reading the index.
But with an IOT Oracle Database places rows in the same block according to the logical order imposed by the index. So if NIU is the first column, all ten rows will be "together" in one block. Or maybe two if you're unlucky. Plus the index has all the information you need. So you save yourself a table lookup.
So instead of 11+ IOs to read 10 rows, you only have 1. This benefit increases the more rows there are per NIU.
If there's always at most one row per NIU the benefit of an IOT is limited. You save yourself a table lookup, but that's about it. But if this is the case, you should have unique constraint on NIU!
If you want to know more, read Martin Widlake's series on these:
https://mwidlake.wordpress.com/2011/07/18/index-organized-tables-the-basics/ Or Richard Foote's:
https://richardfoote.wordpress.com/2012/01/10/index-organized-tables-an-introduction-of-sorts-pyramid-song/ Chris