Oracle Partition by 2 columns Interval - oracle

Im trying to find on google my situation but no one talk about this situation.
i have a table thats is gonna be partitionized with 2 columns.
for 2 columns partitions can anyone show an example for the interval?
In this case i have only one.
For this example how do i use an interval with 2 columns
INTERVAL( NUMTODSINTERVAL(1,'DAY'))
My table:
create table TABLE_TEST
(
PROCESS_DATE DATE GENERATED ALWAYS AS (TO_DATE(SUBSTR("CHARGE_DATE_TIME",1,10),'yyyymmdd')),
PROCESS_HOUR VARCHAR(10) GENERATED ALWAYS AS (SUBSTR("CHARGE_DATE_TIME",12,2)),
ANUM varchar(100),
SWTICH_DATE_TIME varchar(100),
CHARGE_DATE_TIME varchar(100),
CHARGE varchar(100),
)
TABLESPACE TB_LARGE_TAB
PARTITION BY RANGE (PROCESS_DATE, PROCESS_HOUR)
INTERVAL( NUMTODSINTERVAL(1,'DAY'))
Many Thanks,
Macieira

You can't use an interval if your range has more than one column; you'd get: ORA-14750: Range partitioned table with INTERVAL clause has more than one column. From the documentaion:
You can specify only one partitioning key column, and it must be of NUMBER, DATE, FLOAT, or TIMESTAMP data type.
I'm not sure why you're splitting the date and hour out into separate columns (since a date has a time component anyway), or why you're storing the 'real' date and number values as strings; it would be much simpler to just have columns with the correct data types in the first place. But assuming you are set on storing the data that way and need the separate process_date and process_hour columns as you have them, you can add a third virtual column that combines them:
create table TABLE_TEST
(
PROCESS_DATE DATE GENERATED ALWAYS AS (TO_DATE(SUBSTR(CHARGE_DATE_TIME,1,10),'YYYYMMDD')),
PROCESS_HOUR VARCHAR2(8) GENERATED ALWAYS AS (SUBSTR(CHARGE_DATE_TIME,12,2)),
PROCESS_DATE_HOUR DATE GENERATED ALWAYS AS (TO_DATE(CHARGE_DATE_TIME, 'YYYYMMDDHH24')),
ANUM VARCHAR2(100),
SWTICH_DATE_TIME VARCHAR2(100),
CHARGE_DATE_TIME VARCHAR2(100),
CHARGE VARCHAR2(100)
)
PARTITION BY RANGE (PROCESS_DATE_HOUR)
INTERVAL (NUMTODSINTERVAL(1,'DAY'))
(
PARTITION TEST_PART_0 VALUES LESS THAN (DATE '1970-01-01')
);
Table table_test created.
I've also changed your string data types to varchar2 and added a made-up initial partition. process_hour probably wants to be a number type, depending on how you'll use it. As I don't know why you're choosing your current data types it's hard to tell what would really be more appropriate.
I don't really understand why you'd want the partition range to be hourly and the interval to be one day though, unless you want the partitions to be from, say, midday to midday; in which case the initial partition (test_part_0) would have to specify that time, and your range specification is still wrong for that.

Interval partitioning could be built only on one column.
In your case you have proper partition key column - CHARGE_DATE_TIME. Why do you create virtual columns as VARCHAR2? And why do you need to create partition key on them? Interval partitioning could be built only on NUMBER or DATE columns.

Related

Partition by range in oracle using sysdate

I want to create partitions dynamicaly that depends on sysdate, like:
create table partition_interval_demo
( id number(6)
, val varchar2(50)
, date_create date)
partition by range (sale_date) interval (interval '1' day)
( partition p1 values less than ((sysdate-90)));
but I have error like
"ORA-14019: partition bound element must be one of: string, datetime or interval literal, number, or MAXVALUE"
so what can I do here?
You can't do that. Following the documentation
Creating a Range-Partitioned Table
The PARTITION BY RANGE clause of the CREATE TABLE statement specifies
that the table or index is to be range-partitioned. The PARTITION
clauses identify the individual partition ranges, and the optional
subclauses of a PARTITION clause can specify physical and other
attributes specific to a partition segment. If not overridden at the
partition level, partitions inherit the attributes of their underlying
table.
The VALUES LESS THAN clause determines the partition bound: rows with
partitioning key values that compare less than the ordered list of
values specified by the clause are stored in the partition. Each
partition is given a name (sales_q1, sales_q2, ...), and each
partition is contained in a separate tablespace (tsa, tsb, ...).
That means that the range is determined for the key value. sysdate is a function, therefore it is not a value until it is executed. It can't be part of a range partition key value.
Keep in mind the purpose or partitioning. The idea is to split a big segment ( table ) into smaller ones ( partitions ) using a key column. In your case, you want to store in one partition the rows where the key column is less than sysdate-90, which means that today the row will go to one partition, but in one day if the condition sysdate-90 is met, the row should be moved to the other partition.
If you want to do this anyway, although I don't recommend it, you will have to develop your own maintenance process:
The table has keys for each date ( 3 months of partitions for each date)
The Partition others will get everything else, so anything that has no match with any of the existing partition keys.
Every day you will have to retrieve the records that are meeting the condition sysdate-90 and move them to the others partition.
For doing that you need to enable row movement in your table: alter table xx enable row movement
Hope it clarifies
You can use
partition p1 values less than (DATE '2021-01-01')
If your application adds data from let's say 2021-08-01, then Oracle will create single new partition for 2021-08-01, it can deal with gaps or missing days.
If you use SEGMENT CREATION DEFERRED clause (maybe meanwhile the default) then the initial partition does not even consume any space on your disk.

Change the datatype of a column in a partitioned table with billion rows

We have a table with 120 partitions on date range which in turn is subpartitioned again on range.
Each partition has around 200 million records, the conventional way of changing the datatype will make our production unresponsive for hours.Is there any better way for changing the datatype of such a huge table?
We have already tried the following options:
Exchange partition. This does not work.
Create a new table with the same structure as the existing one and the altered column, and inserting the data using /*+ append *. It again takes hours.
Currently the column size is varchar2(30). We need to change it to:
ALTER TABLE ORDERS MODIFY (INFO VARCHAR2(50) );
Changing varchar2(30) to varchar2(50) should work instantly and should not cause any trouble.
You modify just some meta data but actual table data is not touched.

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.

"Dynamic" partitions in oracle 11g

I have a log table with a lot of information.
I would like to partition it into two: first part is the logs from the past month, since they are commonly viewed. Second part is the logs from the rest of the year (Compressed).
My problem is that all the examples of partitions where "up until 1/1/2013", "more recent than 1/1/2013" - That is with fixed dates...
What I am looking for/expecting is a way to define a partition on the last month, so that when the day changes, the logs from 30 days ago, are "automatically" transferred to the compressed partition.
I guess I can create another table which is completley compressed and move info using JOBS, but I was hoping for a built-in solution.
Thank you.
I think you want interval partitions based on a date. This will automatically generate the partitions for you. For example, monthly partitions would be:
create table test_data (
created_date DATE default sysdate not null,
store_id NUMBER,
inventory_id NUMBER,
qty_sold NUMBER
)
PARTITION BY RANGE (created_date)
INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION part_01 values LESS THAN (TO_DATE('20130101','YYYYMMDD'))
)
As data is inserted, Oracle will put into the proper partition or create one if needed. The partition names will be a bit cryptic (SYS_xxxx), but you can use the "partition for" clause to grab only the month you want. For example:
select * from test_data partition for (to_date('20130101', 'YYYYMMDD'))
It is not possible to automatically transfer data to a compressed partition. You can, however, schedule a simple job to compress last month's partition at the beginning of every month with this statement:
ALTER TABLE some_table
MOVE PARTITION FOR (add_months(trunc(SYSDATE), -1)
COMPRESS;
If you wanted to stay with only two partitions: current month and archive for all past transactions you could also merge partitions with ALTER TABLE MERGE PARTITIONS, but as far as I'm concerned it would rebuild the whole archive partition, so I would discourage doing so and stay with storing each month in its separate partition.

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