Partition by range in oracle using sysdate - oracle

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.

Related

Oracle ORA-14028: missing AT or VALUES keyword during SPLIT PARTITION to multiple dat

I'm attempting to split a maxvalue partition (by date) and am ORA-14028: missing AT or VALUES. I tried adding a AT TO_DATE('01-JAN_2018) and still get the error. Given VALUES is in the query any hints or direction is appreciated.
Code being used follows:
ALTER TABLE HISTORY_TABLE SPLIT PARTITION DEFAULT_PARTITION INTO
(
PARTITION P2018Q1 VALUES LESS THAN (TO_DATE('01-APR-2018','dd-MON-yyyy')),
PARTITION P2018Q2 VALUES LESS THAN (TO_DATE('01-JUL-2018','dd-MON-yyyy')),
PARTITION P2018Q3 VALUES LESS THAN (TO_DATE('01-OCT-2018','dd-MON-yyyy')),
PARTITION P2018Q4 VALUES LESS THAN (TO_DATE('01-JAN-2019','dd-MON-yyyy')),
PARTITION P2019Q1 VALUES LESS THAN (TO_DATE('01-APR-2019','dd-MON-yyyy')),
PARTITION P2019Q2 VALUES LESS THAN (TO_DATE('01-JUL-2019','dd-MON-yyyy')),
PARTITION P2019Q3 VALUES LESS THAN (TO_DATE('01-OCT-2019','dd-MON-yyyy')),
PARTITION P2019Q4 VALUES LESS THAN (TO_DATE('01-JAN-2020','dd-MON-yyyy')),
PARTITION P2020Q1 VALUES LESS THAN (TO_DATE('01-APR-2020','dd-MON-yyyy')),
PARTITION P2020Q2 VALUES LESS THAN (TO_DATE('01-JUL-2020','dd-MON-yyyy')),
PARTITION DEFAULT_PART2
)
UPDATE GLOBAL INDEXES;
It appears I am using 12c SQL on 11g causing the error.

Deleting records from Oracle MAX_PARTITION

We have a table with partitions. It also has an overflow partition (max partition) which sorts of acts as a catch-all for records which do not match the partition criteria. The idea was to create the partitions ahead of time so the records never end up in the max_partition. However for one table, this was missed out, so all the records ended up in that single partition.
Now most of these records are not used anymore so they can be deleted. However our approach is to drop the partitions when its too old. This cannot be done in this case. Is there an easy way to handle the purge?
Maybe its an idea to create the partitions now and move the records to them and then drop the partition now, but however it seems like its going to be very poor in performance. The other option was to create a temp table where a subset of records are moved and deleted from there, but again moving the records individually seems time consuming. This table has around 5 million records.
Which would be the best way forward, performance wise. We could manage a little downtime but not much.
We use Oracle 11g.
The table creation script looks something like this:
CREATE TABLE "TRANSACTIONS"
("year" number(4,0) NOT NULL ENABLE)
PARTITION BY RANGE ("year")
(PARTITION "P_OLD" VALUES LESS THAN (2010),
PARTITION "P_2011" VALUES LESS THAN (2011),
...
PARTITION "P_MAX" VALUES LESS THAN (MAXVALUE));
There is no need to drop the partition, you can purge it.
alter table TRANSACTIONS TRUNCATE PARTITION P_MAX UPDATE INDEXES;
or if you prefer, you can also delete the rows:
delete from TRANSACTIONS PARTITION (P_MAX);
You may use INTERVAL partition to make it simpler (actually I don't understand your question):
CREATE TABLE TRANSACTIONS (
...
TRANSACTION_DATE TIMESTAMP(0) NOT NULL
)
PARTITION BY RANGE (TRANSACTION_DATE) INTERVAL (INTERVAL '12' MONTH)
(PARTITION P_OLD VALUES LESS THAN (TIMESTAMP '2000-01-01 00:00:00' ) )
ENABLE ROW MOVEMENT;

After insert data to a partitioned table, why the num_rows is 0 in user_tab_partitions?

Question 1. Is the data not insert to the specified partition successfully?
As I need to delete the specified partition, so I need make sure the insert into specified partition must successfully although the num_rows is 0 in user_tab_partitions.
Question 2. I have googled some ideas, some say I need to analyze the table.
why need to analyze?
Question 3.
if I analyze the table, will the performance be different compared to not analyzing it?
PARTITION BY RANGE ( CYCLE_MTH ) INTERVAL ( numtoyminterval(1,'MONTH') )
(
PARTITION per_limra_p004
VALUES LESS THAN ( TO_DATE('01-11-2015','DD-MM-YYYY') ),
PARTITION per_limra_p003
VALUES LESS THAN ( TO_DATE('01-12-2015','DD-MM-YYYY') ),
PARTITION per_limra_p002
VALUES LESS THAN ( TO_DATE('01-01-2016','DD-MM-YYYY') ),
Oracle doesn't constantly update its statistics about tables and partitions, as this would be too heavy. Once you analyze the table, the statistics (e.g., those in user_tab_partitions) will be updated. Since Oracle now has different statistics, the optimizer may very well create different execution plans for your queries, and thus affect performance.
NUM_ROWS shows the number of rows based on last analyze, so you should not rely on it. I suggest to run a count on it, e.g.
SELECT COUNT(*)
FROM your_table PARTITION FOR (DATE '2016-04-01')
or
SELECT COUNT(ROWNUM)
FROM your_table PARTITION FOR (DATE '2016-04-01')
WHERE ROWNUM <= 1
It should be much faster than DBMS_STATS.GATHER_TABLE_STATS (provided you have any index on this table)
Based on the partition information you've added to your question, the answer is clear. The reason that
SELECT COUNT() FROM per_limra PARTITION FOR (DATE '2015-06-01')
and
SELECT COUNT() FROM per_limra PARTITION FOR (DATE '2015-05-01')
both return 1 is that they're both looking at the same partition. Your three partitions are
PARTITION per_limra_p004
VALUES LESS THAN ( TO_DATE('01-11-2015','DD-MM-YYYY'), i.e. less than 01-Nov-2015
PARTITION per_limra_p003
VALUES LESS THAN ( TO_DATE('01-12-2015','DD-MM-YYYY'), i.e. less than 01-Dec-2015
PARTITION per_limra_p002
VALUES LESS THAN ( TO_DATE('01-01-2016','DD-MM-YYYY'), i.e. less than 01-Jan-2016
The dates in your queries are DATE '2015-06-01', or 01-Jun-2015, and DATE '2015-05-01', or 01-May-2015, both of which fall into the range for partition per_limra_p004, which contains data for all dates prior to 01-Nov-2015. Thus, both queries return data from the same partition, which is why they return the same value.
Best of luck.

How do I alter my existing table to create a range partition in Oracle

I have existing table which has 10 years of data (I have taken dump).
I would like to Range partition the existing table on one date key column within the table.
Most of the examples I see are with CREATE TABLE..PARTITION BY RANGE... to add new partitions. But my table is existing table.
I assume I need some ALTER statement.
ALTER TABLE TABLE_NAME
PARTITION BY RANGE(CREATED_DATE)
PARTITION JAN16 VALUES LESS THAN (01-02-2016),
PARTITION FEB16 VALUES LESS THAN (01-03-2016) AND GREATER THAN(31-01-2016),//OR?
PARTITION MAR16 VALUES BETWEEN (01-03-2016) AND (31-03-2016), //OR?
Two questions..
Do I need Alter statement to add partitioning mechanism or need to work with create statement?
What is the proper syntax for keeping each partition having only ONE MONTH data.
If you are using Oracle 12c Release 2 you could use single ALTER to convert non-partitioned table to partitioned one (this is one way trip):
CREATE TABLE my_tab ( a NUMBER(38,0), b NUMBER(38,0));
ALTER TABLE MY_TAB MODIFY PARTITION BY RANGE (a) INTERVAL (1000) (
PARTITION p1 VALUES LESS THAN (1000)) ONLINE;
You could convert indexes too, adding:
update indexes (index_name [local/global]);
db<>fiddle demo
Beacuse your table non-partitioned you have two options:
Export data, drop table, create new patitioned table, import data.
Use split then exchange partition method. https://oracle-base.com/articles/misc/partitioning-an-existing-table-using-exchange-partition
Also, if you want new partition per month read about SET INTERVAL. For example:
CREATE TABLE tst
(col_date DATE)
PARTITION BY RANGE (col_date) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(PARTITION col_date_min VALUES LESS THAN (TO_DATE('2010-01-01', 'YYYY-MM-DD')));

Oracle Partition by 2 columns Interval

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.

Resources