Oracle multicolumn partitioning vs using a subpartition - oracle

Apart from the obvious, can anyone explain the what is different between multicolumn partitioning and using a subpartition? And which one is better for a OLTP scenario? For details, see Managing Partitioned Tables and Indexes in the Oracle Database Administrator's Guide.
A (dumb) example of a table partitioned on multiple columns is:
CREATE TABLE demo1
(
year NUMBER,
month NUMBER,
day NUMBER,
instance NUMBER, /* assuming this can only be 1 or 2 */
other1 VARCHAR2(50),
other2 VARCHAR2(50),
other3 VARCHAR2(50)
)
PARTITION BY RANGE (year,instance)
(
PARTITION data_2009_inst1 VALUES less than (2009,2) TABLESPACE data_2009,
PARTITION data_2009_inst2 VALUES less than (2009,3) TABLESPACE data_2009,
PARTITION data_2010_inst1 VALUES less than (2010,2) TABLESPACE data_2010,
PARTITION data_2010_inst2 VALUES less than (2010,3) TABLESPACE data_2010,
PARTITION data_2011_inst1 VALUES less than (2011,2) TABLESPACE data_2011,
PARTITION data_2011_inst2 VALUES less than (2011,3) TABLESPACE data_2011
);
Similarly, example of a subpartitioned table is:
CREATE TABLE demo2
(
year NUMBER,
month NUMBER,
day NUMBER,
instance NUMBER, /* assuming this can only be 1 or 2 */
other1 VARCHAR2(50),
other2 VARCHAR2(50),
other3 VARCHAR2(50)
)
PARTITION BY RANGE (year)
SUBPARTITION BY LIST (instance) /* Cannot subpartition by range in 10gR2 */
SUBPARTITION template
(
SUBPARTITION i1 VALUES (1),
SUBPARTITION i2 VALUES (2),
SUBPARTITION ix VALUES (DEFAULT)
)
(
PARTITION data_2009 VALUES less than (2010) TABLESPACE data_2009,
PARTITION data_2010 VALUES less than (2011) TABLESPACE data_2010,
PARTITION data_2011 VALUES less than (2012) TABLESPACE data_2011
);
Now what is the difference between these tables? Are they not "logically" the same? I know its far easier to add partitions to demo2 as you need to split partitions on demo1 to get more partitions as time passes by. Which on is better in an OLTP scenario?
On a side note, the reason I am partitioning on the INSTANCE number has to do with Oracle RAC. I am trying to create an "instance affinity" to stop "hot block" from slowing down the database as these need be sent across the interconnect between the RAC nodes. (We have empirically proved that this does make a difference in our testing).

There probably isn't any difference in your case, but in general sub-partitioning allows you to partition in 2 different ways, such as range-hash, range-list. Your sub-partition example is range-list, but equivalent to the single-level range partitioning. However, you could not use a single-level if your sub-partitioning was like this example from the doc you linked:
ALTER TABLE quarterly_regional_sales
ADD PARTITION q1_2000 VALUES LESS THAN (TO_DATE('1-APR-2000','DD-MON-YYYY'))
STORAGE (INITIAL 20K NEXT 20K) TABLESPACE ts3 NOLOGGING
(
SUBPARTITION q1_2000_northwest VALUES ('OR', 'WA'),
SUBPARTITION q1_2000_southwest VALUES ('AZ', 'UT', 'NM'),
SUBPARTITION q1_2000_northeast VALUES ('NY', 'VM', 'NJ'),
SUBPARTITION q1_2000_southeast VALUES ('FL', 'GA'),
SUBPARTITION q1_2000_northcentral VALUES ('SD', 'WI'),
SUBPARTITION q1_2000_southcentral VALUES ('OK', 'TX')
);

One advantage of sub-partitions is that they allow individual fine-grained management of the sub-partitions. For example in a data archive table lets say there are different retention requirements based not only on the date, but another value as well.
Using your example perhaps you are required to keep data with value instance = 1 for 7 years, but data with instance = 2 can be discarded after 4 years. Sub-partitioning would allows you to drop the sub-partitions containing data with instance = 2 independently of the other values.

Related

Oracle Partitioning Split Partition Issue

We have a VLDB around 2TB and tables are partitioned on each date. We also have index mapping for these partitions. But for year 2019 all the data up to 01-Oct-2019 is getting into a single partition as due to a typo a single sub partition got created with a high value of 01/10/2019.
We are trying to correct this using Split Partition i.e. move data up to 10th Feb 2019 to single partition and then remove the empty partition and create single partitions for each date. This is working fine.
The issue is coming with index mapping as there are mappings already from 1st Oct to 31st Dec and we are not able to drop these. So we were trying to create mapping from 11 Feb till 30 Sep. The script for this is taking a very long time due to volume of data. And also the indexes goes into UNUSABLE state after this and when we try to rebuild it's taking ages!!
Is there any better way we can do it.
CODE SAMPLE-
CREATE TABLE My_Table (
id NUMBER(9,0) NOT NULL,
source_system VARCHAR(20),
eod_date NUMBER(9,0) NOT NULL,
other columns
)
TABLESPACE XYZ001td
PARTITION BY LIST (source_system)
SUBPARTITION BY RANGE (
eod_date
)
(
PARTITION p_XYZ VALUES ('XYZ')
NOCOMPRESS
(
SUBPARTITION XYZ_20181227 VALUES LESS THAN (20181228) TABLESPACE XYZ_20181227_td,
SUBPARTITION XYZ_20181228 VALUES LESS THAN (20181229) TABLESPACE XYZ_20181228_td,
SUBPARTITION XYZ_20181229 VALUES LESS THAN (20181230) TABLESPACE XYZ_20181229_td,
SUBPARTITION XYZ_20181230 VALUES LESS THAN (20181231) TABLESPACE XYZ_20181230_td,
SUBPARTITION XYZ_20181231 VALUES LESS THAN (20190101) TABLESPACE XYZ_20181231_td
**SUBPARTITION XYZ_20191001 VALUES LESS THAN (20191002) TABLESPACE XYZ_20191001_td,**
SUBPARTITION XYZ_20191002 VALUES LESS THAN (20191003) TABLESPACE XYZ_20191002_td,
SUBPARTITION XYZ_20191003 VALUES LESS THAN (20191004) TABLESPACE XYZ_20191003_td,
)
)
/
CREATE INDEX inx_my_table_01
ON My_table (
source_system,
eod_date
)
TABLESPACE XYZ001td
GLOBAL PARTITION BY HASH (
source_system,
eod_date
)
(
PARTITION XYZ_20181227
TABLESPACE XYZ_20181227_ti
LOGGING,
PARTITION XYZ_20181228
TABLESPACE XYZ_20181228_ti
LOGGING,
PARTITION XYZ_20181229
TABLESPACE XYZ_20181229_ti
LOGGING,
PARTITION XYZ_20181230
TABLESPACE XYZ_20181230_ti
LOGGING,
PARTITION XYZ_20181231
TABLESPACE XYZ_20181231_ti
LOGGING
)
i am not sure if i completely understand the problem. But i think following steps might help
Create a temp table and copy all 2019 data to that table
drop all 2019 partitions and indexes.
recreate partitions and re insert the data
create local indexes
We can not drop global hash-partitioned index
ORA-14330: Cannot drop a partition of a global hash-partitioned index
Is there any way to insert intermediate Index mapping without touching existing Index mapping

Composite Interval Partitioning - First by month and subpartition by date - Oracle 11g R2

We have to pull snapshot data from the source(SQL Server) to target(Oracle). The data in the source is mostly appended everyday although there could be few deletes and updates. Since the data is updated and deleted in the source, it would be hard to know what data we had on a particular date in the past unless we pull everyday snapshot data into the target by having a new field "as_of_dt". Considering the reporting requirements on top of the target table, it is better to partition by month and subpartition by date.
I can create the table as below -
CREATE TABLE SUBPART_TABLE
(
AS_OF_DT DATE,
AS_OF_DAY NUMBER(2,0) GENERATED ALWAYS AS (EXTRACT(DAY FROM AS_OF_DT)) VIRTUAL,
... more COLUMNS
)
partition by range (AS_OF_DT) interval (numtoyminterval(1,'MONTH'))
SUBPARTITION BY LIST (AS_OF_DAY)
SUBPARTITION TEMPLATE
(SUBPARTITION P01 VALUES (1),
SUBPARTITION P02 VALUES (2),
SUBPARTITION P03 VALUES (3),
...
SUBPARTITION P30 VALUES (30)
SUBPARTITION P31 VALUES (31)
)
(
partition P201706 values less than (to_date('2017-07-01','yyyy-mm-dd'))
);
Looking at the explain plan, I figured that I would have to include the below two filters for Oracle to do only a single subpartition scan -
WHERE AS_OF_DT = TO_DATE('2017-06-15', 'yyyy-mm-dd')
AND AS_OF_DAY = 15;
How can I partition by month and subpartition by date using the same date field (in this case - "as_of_dt") so that I don't have to include an additional filter for Oracle to scan only the subpartition belonging to that date ?
If you're wondering why I'm not partitioning on just date rather than partitioning on month and then subpartioning on date, the reason is there would be too many main partitions (365/year) which I don't think is good idea.

How to store range/hash composite partitions in separate datafiles by range?

I'm creating a database which will utilize composite partitioning. I will partition one table using range partitioning (by date)
and then further subpartition it by hash (by client id). So far so good, no problem, but I also need to have those partitions
stored in separate data files each dbf holding data for a single month. I'm reading on composite partitions and what I found
is that primary range partitioning will be only a logical one and data will be stored in subpartitions instead which seems to
make my goal impossible. Am I right and should look for a different solution?
Thanks in advance.
My databases are Oracle 11g and Oracle 12
On existing table you can move partitions or subpartitions to a different tablespace, i.e. different datafile, examples:
ALTER TABLE scuba_gear MOVE SUBPARTITION bcd_types TABLESPACE tbs23;
ALTER TABLE parts MOVE PARTITION depot2 TABLESPACE ts094;
see Moving Subpartitions and Moving Table Partitions
For new tables typically you would be create them like this:
CREATE TABLE sales
( prod_id NUMBER(6)
, cust_id NUMBER
, time_id DATE
, channel_id CHAR(1)
, promo_id NUMBER(6)
, quantity_sold NUMBER(3)
, amount_sold NUMBER(10,2)
)
PARTITION BY RANGE (time_id)
INTERVAL (NUMTOYMINTERVAL(1,'MONTH'))
STORE IN (ts_1, ts_2, ts_3, ts_4, ts_5, ts_6 ,ts_7 ,ts_8, ts_9, ts_10, ts_11, ts_12)
SUBPARTITION BY HASH (cust_id) SUBPARTITIONS 4
( PARTITION before_2000 VALUES LESS THAN (TO_DATE('01-JAN-2000','dd-MON-yyyy')));
Oracle then will put the monthly partitions by "round-robin" method to these 12 tablespaces. STORE IN clause is also possible for subpartitions, see Creating a composite range-hash partitioned table

Best way to design for one-to-many code values in Oracle

We receive several millions of records per day on temperature metrics. Most of the pertinent metadata for these records is maintained in a single partitioned table by date (month). We are going to start receiving up to 20 individual codes associated with this data. The intent is to ultimately allow searching by these codes.
What would be the most effective way to store this data to minimize search response time? The database is Oracle 11gR2.
Some options I was taking into consideration:
(1) Create a separate table with main record id and code values. Something like
id code
-- ----
1 AA
1 BB
1 CC
2 AA
2 CC
2 DD
Concerns:
would probably require a bitmap index on the code column, but the table is highly transactional so no bitmap
table would get huge over time with up to 20 codes per main record
(2) Create a separate table partitioned by code values.
Concerns:
partition maintenance for new codes
search performance
(3) Add a XMLType column to existing table and format the codes for each record into XML and create an XMLIndex on the column: Something like:
<C1>AA</C1>
<C2>BB</C2>
<C3>CC</C3>
Concerns:
query response time when searching on CODE probably would be poor
Any recommendations are welcome.
Thanks.
You need to benchmark different approaches. There's no way we can give you meaningful solutions without knowing much more about your scenario. How many different codes will there be in total? What's the average number of codes per reading? Will there be a noticeable skew in the distribution of codes? What access paths do you need to support for searching by code?
Then there's the matter of how you load data (batches? drip feed?). And what benefits you derive from using partitioning.
Anyway. Here's one more approach, which is an amalgamation of your (1) and (2).
Given that your main table is partitioned by month you should probably partition any child table with the same scheme. You can subpartition by code as well.
create table main_codes
( reading_dt date not null
, main_id number not null
, code varchar2(2)
, constraint main_codes_pk primary key (code, reading_dt, main_id) using index local
)
partition by range (reading_dt)
subpartition by list (code)
subpartition template
(
subpartition sp_aa values ( 'AA' ),
subpartition sp_bb values ( 'BB' ),
subpartition sp_cc values ( 'CC' ),
subpartition sp_dd values ( 'DD' )
)
(
partition p_2015JAN values less than (date '2015-02-01' ),
partition p_2015FEB values less than (date '2015-03-01' ),
partition p_2015MAR values less than (date '2015-04-01' )
)
/
You'll probably want a foreign on the main table too:
alter table main_codes
add constraint code_main_fk foreign key (reading_dt, main_id)
references main_table (reading_dt, main_id)
/
create index code_main_idx on main_codes (entry_dt, id) local
/
Depending on the number of codes you have, creating the subpartition template could be tedious. This is why Nature gave us cut'n'paste.
But whatever you do, don't go down the XML path.

Oracle automatic partitioning by day

I'm working with an Oracle 11g DB that has an input of 3-5m rows a day. In the future I would like to use partitioning based on the column Timestamp. My goal is to create a new partition for every day, automatically.
I just found ways to create a given range of days i.e. 1-20 but not for a unlimited time (01.01.2014 to mm.dd.yyyy).
For daily ranges you can do it like this:
create table ...
...
interval(numtodsinterval(1, 'DAY'))
(
partition log_data_p1 values less than (to_date('22-04-2015', 'DD-MM-YYYY')),
partition log_data_p2 values less than (to_date('23-04-2015', 'DD-MM-YYYY'))
);
Important to use numtodsinterval instead of numtoyminterval
Oracle 11g does offer automatic partition creation, you just need to create table with proper syntax like this one:
create table
pos_data (
start_date DATE,
store_id NUMBER,
inventory_id NUMBER(6),
qty_sold NUMBER(3)
)
PARTITION BY RANGE (start_date)
INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION pos_data_p2 VALUES LESS THAN (TO_DATE('1-7-2007', 'DD-MM-YYYY')),
PARTITION pos_data_p3 VALUES LESS THAN (TO_DATE('1-8-2007', 'DD-MM-YYYY'))
);
Here, two partitions have been defined and an interval of one month has been specified. If date goes beyond the max date specified in partition then oracle automatically creates new partition.
Similarly you can specify partition for day range and oracle will take care of rest.

Resources