Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Matt.

Asked: October 11, 2002 - 8:35 pm UTC

Last updated: July 06, 2009 - 7:26 pm UTC

Version: 9.0.1.4

Viewed 1000+ times

You Asked

I am looking to improve the performance of an existing DB that relies heavily on an Oracle feature called Partition Views.

What we have many pairs of large tables (approx 10Gb each) that store interval based data on a monthly basis. Each table stores data according to a different interval value. The interval data is assocatited with an sequenced ID. We have enough of these tables to store data for several years online. Queries must be able to access interval data for a single id or set of id's for the entire range of data.

Currently we have a cluster index defined on each pair of monthly tables (as these tables are regularly joined together).

To allow an efficient query accross all the years of data the parameter PARTITION_VIEWS_ENABLED is set to true in the init.ora and several views are used to UNION ALL the various tables.

Can you please explain to me how the partition view functionality works? I have gathered some trace files and expected to see tables excluded from queries depending on the range of data requested. However the trace just reveals the view query.

Secondly can you briefly highlight a solution that is based on 9i functionality. I would like to introduce table partitions but am aware that clusters cannot be used here. I am also concerned about the volume of data that must be converted.

Finally can you summarise any concerns or potential issues that you might have with this approach?

Many Thanks.

and Tom said...

Partitioned views were used briefly in version 7.3 before partitioning was available.

Post 7.3, partitioned views are

o supported
o not recommended
o not being enhanced at all


Partitioning would be the appropriate technique here.

Now, with the partition view query plans -- you would expect to see ALL of the tables referenced in the plan (just like in most partitioned query plans as well) -- but at runtime, certain tables would be queried or not queried depending on the inputs (binds) to the query plan itself. You can observe this by offlining a tablespace for example that has some of the tables contributing to the view -- and then running a query you know should not need to query that table. If it runs, well, then that table was eliminated from consideration at runtime (but it'll appear in the plan, it has to -- we might need to query it given different inputs)


Partitioned tables would be the way to go for further enhancements and development. Converting a 10gig table into a partition should only take a couple of minutes on a reasonably fast system these days.


My concern with the "partitioned view" is that it might go away in the near future, development on it stopped in about 1995, and partitioning is our future direction.



Rating

  (10 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Followup

Matt, October 13, 2002 - 2:59 am UTC

Yep, we recently upgraded from 7.3 all the way up to 9.0.1.4.0 which is why this functionality is in place.

I think that Oracle has already identified that this functionality will be de-supported / deprecated in thenext release after 9.2.

Can you reference any Oracle documentation on the limitations of Partitioned tables? For instance how many partitions may such a table have?

Say I partition the data in a different way - by week say - I would have 364 partitions for each table type (or over 700 if I decide to stick all the (7 years of) data together). Is this possible?

(obviously we would need to test each of these approaches).

Oh, we are currently RBO and on the way to CBO. From memory I believe that we need to get to CBO before we can really take advantage of partitions. Can you confirm this?


One final question. I have a requirement to summarise(aggregate) this data for additional analysis. That is all the 7 years of data. Although most of the queries would run accross the first year of data (this is where all the data changes). The requirement is to query the summarised data and then drill into the underlying raw data according to the results of the initial analysis. I am intending to implement this in a seperate schema in the same instance (depending on whether the machine can handle this) but am curious to find out what is the best way to summarise this huge dataset. I am thinking that the older data would be summarised in a materialised view and the newer data would be summarised at runtime (to always pick up the latest data), I would need a 'rolling window' mechanism so that these two query types are altered according to the dates associated with the data.

Is there some way that I can partition materialised views?

Could you please comment on my proposed approach and highlight any other functionality that might be better suited to my explanation of the requirements?

Best Regards.

Tom Kyte
October 14, 2002 - 7:33 am UTC

See appendix C in your 7.3 "Oracle7 Server Tuning" guide. There are no physical, only practical (the view statement gets rather large, parsing will increase over time as you have hundreds of referenced objects/indexes and so on)

As soon as you access a partitioned table (truly partitioned) you will be using CBO.

Hundreds, thousands of partitions are doable in a truly partitioned table.


MV's can be partitioned yes, it just part of the syntax (see the CREATE MATERIALIZED view). to support incremental (fast) refreshs, you would create MV logs on the base detail data is all.

Many thanks.

Matt Butler, October 14, 2002 - 10:20 pm UTC

Thanks for the reference. This covers all the information I had wanted to know (but could not find).

The 7.3 database reference lists the limit of SQL statement size as 64K (same as 9i) which, I guess will be the limiting factor on the views (alongside those you mention).

The 9i Reference indicates that the limit on partitions per table is 64K - 1. ie: alot.

"As soon as you access a partitioned table (truly partitioned) you will be using CBO." -

My undertanding is then that we *could* run RBO and still get benefits from partitioning (provided we have sufficiently up to date stats for the CBO)?

Thanks for the information about the MV's. I think that these will be very useful - particulary the fast refresh.

Tom Kyte
October 15, 2002 - 7:41 am UTC

the 64k "limit" is not really a limit, it can go much much much larger. However, the parse time will get much much much larger as the number of objects referenced in the query gets really big.


If you reference a partitioned table in a query the CBO will be used, the RBO cannot in any way shape or form be use for that query. Other queries could use RBO but anything that touched that table -- CBO.




Agreed

Matt, October 15, 2002 - 7:42 pm UTC

Yep, this confirms my understanding.

Many Thanks

Do you still keep the same opinion on 10g about partitioned views

juancarlosreyesp@oracleboliviaspecialists.com, September 15, 2006 - 11:58 am UTC

Hi Tom, please
About your answer:
Post 7.3, partitioned views are
o supported
o not recommended
o not being enhanced at all
Do you still keep the same opinion on 10g about partitioned views?
I had been reading about partition pruning (on oracle standard edition), and I'm some confused about it, could you clarify it.

Thanks Tom :)

Tom Kyte
September 15, 2006 - 12:09 pm UTC

the facts surrounding partitioned views remain the same today as they did when first stated - yes.

however, if you have standard edition, then partitioning is not an option and "partitioned views" are the only game in town.

A reader, September 15, 2006 - 1:22 pm UTC

Thanks Tom
I found this excellent blog from David Aldridge
I found your comment on this blog
</code> http://oraclesponge.wordpress.com/2005/08/20/partition-not-quite-pruning/ <code>

1)

I suffer some level dumbness, could you help me to understand the use of this concept on oracle standard edition

if I have for example

Employment_payment and I divide on

Employment_payment_2004
Employment_payment_2005
Employment_payment


2)
Because there is not too much documentation about it on the internet, could you give a link or an in deep example of benefit of partitioned views, because I think I done it incorrectly, because when I query something on a partitioned view tables, the optimizer read both indexes, not only the index to the table belonging to the range I'm querying; that is the reason I'm thinking to use function tables, if I can get only to read the indexes of the table of what I'm querying, you'll save a lot of work.

Thanks :)

Tom Kyte
September 15, 2006 - 1:55 pm UTC

1) what concept? He is using partitioning, you do not have that as an OPTION at all on SE, you cannot use partitioning.


2) the only link to partitioned views is the 7.3 link I gave you. Not documented as anything "special" after that version.

A reader, September 15, 2006 - 4:42 pm UTC

Thanks Tom :)

A reader, September 15, 2006 - 7:14 pm UTC

Tom I was testing on 10.2 oracle partition and is really working nice,
Specifically talking on performance, not functionality neither partitions administration.

Which performance benefit you get on oracle enterprise partition option, you don't get using partition views. I had tested and I get the same get querying directly the table or through a view using partition views, this solves my problem of not having partitions.

As I told you I am decided to create me my own partition packages, and I'll appreciate your advise.

Thanks :)

Tom Kyte
September 16, 2006 - 2:35 pm UTC

partitioning solves the "parsing and optimization problem" that you'll run into with partitioned views. (each view is a separate object, it is a nasty complex query with tons of objects - not so with partitions). You don't have partition wise joins with them. You dont have the partition DDL commands. It is just less functional.

For small situations (a very small set of partitions) they can be nearly as effective, but "in manual mode".

A reader, September 18, 2006 - 9:14 am UTC

Thanks Tom,
I think a package can solve some DDL issues, and a function table will help in creating global indexes, to avoid unnecesary partition access when not using primary keys on the query :)

10g support of partitioned views is very limited

Russell, April 04, 2007 - 10:30 am UTC

Hi,

Given the 'PARTITION_VIEW_ENABLED' parameter is obsolete in 10.1 are the views of any real use anymore?

It seems that the filter element of the explain plan no longer gets added and so all tables are being considered..

Example on 10.1.0.4:

create table line_item_1992  (
   constraint C_send_date_1992
   check(send_date < '01-Jan-1993')
   disable,
      order_key         number ,
      part_key          number ,
      source_key        number ,
      send_date         date ,
      promise_date      date ,
      receive_date      date );
create table line_item_1993  (   
   constraint C_send_date_1993
   check(send_date > '01-Jan-1993' and send_date < '01-Jan-1994')   
   disable,
     order_key          number ,
     part_key           number ,
     source_key         number ,
     send_date          date ,
     promise_date       date ,
     receive_date       date );

create index part_key_source_key_1992
   on line_item_1992 (part_key, source_key);
create index part_key_source_key_1993
   on line_item_1993 (part_key, source_key);

analyze table line_item_1992 compute statistics;
analyze table line_item_1993 compute statistics;

create or replace view line_item as
   select * from line_item_1992 union all
   select * from line_item_1993;
   
explain plan for select * from line_item
   where receive_date = 'Feb-01-1992';

select substr (
   lpad (' ',2*(level-1))||decode(id,0,statement_id,operation)
   ||' '||options||' '||object_name, 1, 79) "plan steps"
   from plan_table
   start with id = 0
   connect by prior id = parent_id; 
   
plan steps
--------------------------------------------------------------------------------
  VIEW  LINE_ITEM
    UNION-ALL PARTITION
      TABLE ACCESS FULL LINE_ITEM_1992
      TABLE ACCESS FULL LINE_ITEM_1993


The Oracle 7.3 doc indicates the explain plan should look like:

VIEW LINE_ITEM
UNION_ALL PARTITION
TABLE ACCESS FULL LINE_ITEM_1992
FILTER
TABLE ACCESS FULL LINE_ITEM_1993

Is this to force users upto Enterprise edition? Seems a little harsh as 10g Standard Edition has no paritioning option.
Tom Kyte
April 04, 2007 - 11:28 am UTC

ops$tkyte%ORA10GR2> select * from line_item where receive_date = 'Feb-01-1992';

Execution Plan
----------------------------------------------------------
Plan hash value: 972573335

------------------------------------------------------------------------
| Id  | Operation            | Name           | Rows  | Bytes | Cost (%C
------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                |     1 |    66 |     2
|   1 |  VIEW                | LINE_ITEM      |     1 |    66 |     2
|   2 |   UNION-ALL PARTITION|                |       |       |
|*  3 |    TABLE ACCESS FULL | LINE_ITEM_1992 |     1 |    66 |     2
|*  4 |    TABLE ACCESS FULL | LINE_ITEM_1993 |     1 |    66 |     2
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("RECEIVE_DATE"='Feb-01-1992')
   4 - filter("RECEIVE_DATE"='Feb-01-1992')

ops$tkyte%ORA10GR2> set autotrace off



they do what they always did - use dbms_xplan to display the plan table (10.2 does that with autotrace)....

DIY "partitioned view" partition pruning

Christian Veit, July 02, 2009 - 8:50 am UTC

Hi Tom,

I did read your warning "only truly new question...", but I dare write this anyway.
I think it is closely related,
and it might be useful for others if it is correct what I think I found.

This does work with dual and with some other "real" tables I tested with,
also with some more complex queries (joins) as the parts of the union all.


Are my findings / assumptions right, would this technique work: would always only the matching parts of the union all actually get executed?


Oracle 10g Release 10.2.0.4.0
-- Testcase 1: Do it yourself "Partitioned View" partition elimination"
-- shall prove that only the matching parts ot the union actually get executed.
-- Reason: optimizer recognizes the "sort-of-constraint" filter clause in the 
-- where clause of each part of the union all. 
select * -- select * as column dummy is important to prove testcase 
from
(   -- part 1 'Hello'
    select *
    from (select 'Hello' as greeting, to_number('a') as dummy from dual)
    where greeting = 'Hello' -- "sort-of-constraint"
union all
    -- part 2 'Hi'
    select *
    from (select 'Hi' as greeting, to_number('a') as dummy from dual)
    where greeting = 'Hi' -- "sort-of-constraint"
union all   
    -- part 3 'Hello' 
    select *
    from (select 'Allo' as greeting, to_number(null) as dummy from dual)
    where greeting = 'Allo' -- "sort-of-constraint"    
)
-- test each value of column greeting: 'Hello','Hi','Allo', and no where-clause
-- expected result:
--  'Hello':         exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  'Hi':            exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  no where clause: exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  'Allo'  no exception, result displayed
-- any other value: no exception, empy resultset displayed
where greeting = 'Allo' -- ok 
;


-- Testcase 2: do it yourself "Partitioned View" partition elimination"
-- shall prove that only the matching parts of the union actually get executed.
-- Reason: optimizer recognizes the constants in each part of the union all, 
-- so the "sort-of-constraint" are not even needed
select * -- "select *" as column dummy is important to prove testcase 
from
(   -- part 1 'Hello'
    select 'Hello' as greeting, to_number('a') as dummy from dual
union all
    -- part 2 'Hi'
    select 'Hi' as greeting, to_number('a') as dummy from dual
union all   
    -- part 3 'Hello' 
    select 'Allo' as greeting, to_number(null) as dummy from dual
)
-- test each value of column greeting: 'Hello','Hi','Allo', and no where-clause
-- expected result:
--  'Hello':         exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  'Hi':            exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  no where clause: exception ORA-01722: invalid number (because of column dummy: to_number('a'))
--  'Allo'  no exception, resultset displayed
-- any other value: no exception, empy resultset displayed
where greeting = 'Allo'
;


Many Thanks
Christian
Tom Kyte
July 06, 2009 - 7:26 pm UTC

... Are my findings / assumptions right, would this technique work: would always only the matching parts of the union all actually get executed? ...

never say never, never say always, I always say


"would ALWAYS only..."

No, I cannot say that, I can say - the plan would show you if it did or not.

ops$tkyte%ORA10GR2> create or replace view v
  2  as
  3  select *
  4  from
  5  (   select * from (select 'Hello' as greeting, to_number('a') as dummy from dual) where greeting = 'Hello'
  6  union all
  7      select * from (select 'Hi' as greeting, to_number('a') as dummy from dual) where greeting = 'Hi'
  8  union all
  9      select * from (select 'Allo' as greeting, to_number(null) as dummy from dual) where greeting = 'Allo'
 10  )
 11  /

View created.

ops$tkyte%ORA10GR2> delete from plan_table;

0 rows deleted.

ops$tkyte%ORA10GR2> explain plan for select * from v where greeting = 'Hello';

Explained.

ops$tkyte%ORA10GR2> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
Plan hash value: 1780069912

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     3 |    51 |     2   (0)| 00:00:01 |
|   1 |  VIEW            |      |     3 |    51 |     2   (0)| 00:00:01 |
|   2 |   UNION-ALL      |      |       |       |            |          |
|   3 |    FAST DUAL     |      |     1 |       |     2   (0)| 00:00:01 |
|*  4 |    FILTER        |      |       |       |            |          |
|   5 |     FAST DUAL    |      |     1 |       |     2   (0)| 00:00:01 |
|*  6 |    FILTER        |      |       |       |            |          |
|   7 |     FAST DUAL    |      |     1 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter(NULL IS NOT NULL)
   6 - filter(NULL IS NOT NULL)

20 rows selected.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> delete from plan_table;

8 rows deleted.

ops$tkyte%ORA10GR2> explain plan for select * from v where greeting = 'Allo';

Explained.

ops$tkyte%ORA10GR2> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
Plan hash value: 47571485

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     3 |    51 |     2   (0)| 00:00:01 |
|   1 |  VIEW            |      |     3 |    51 |     2   (0)| 00:00:01 |
|   2 |   UNION-ALL      |      |       |       |            |          |
|*  3 |    FILTER        |      |       |       |            |          |
|   4 |     FAST DUAL    |      |     1 |       |     2   (0)| 00:00:01 |
|*  5 |    FILTER        |      |       |       |            |          |
|   6 |     FAST DUAL    |      |     1 |       |     2   (0)| 00:00:01 |
|   7 |    FAST DUAL     |      |     1 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter(NULL IS NOT NULL)
   5 - filter(NULL IS NOT NULL)

20 rows selected.




see the filters in there - they show the subtrees that are pruned.

However, I would not RELY on this always being true (eg: with your to_number conversions). The optimizer is free to change the plan and that could make your query unsafe.

More to Explore

VLDB

If you are new to partitioning, check out Connor McDonald's introduction series here.

VLDB

Documentation set on VLDB and Partitioning.