Wanted to create partition table but getting syntax error:
CREATE TABLE public.emp (id int, date_1 date, amt decimal(10,2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (extract(year from date_1)::int)
( START ('2008') INCLUSIVE
END ('2010') EXCLUSIVE
EVERY (INTERVAL '1 year')
);
ERROR: syntax error at or near "("
LINE 3: PARTITION BY RANGE (extract(year from date_1)::int)
^
********** Error **********
ERROR: syntax error at or near "("
SQL state: 42601
Character: 117
'partition by range' expects a column name as key.
Try:
partition by range (date_1)
(START (date '2008-01-01') INCLUSIVE
END (date '2009-01-01') EXCLUSIVE
EVERY (INTERVAL '1 year')
)
If you want a name to each partition, then you have to give explicitly each partition name.
CREATE TABLE public.emp (id int, date_1 date, amt decimal(10,2))
DISTRIBUTED BY (id)
partition by range (date_1)
(partition year2008 START (date '2008-01-01') INCLUSIVE,
partition year2009 START (date '2009-01-01') INCLUSIVE,
partition year2010 START (date '2010-01-01') INCLUSIVE
END (date '2011-01-01') EXCLUSIVE EVERY (INTERVAL '1 year') );
Then following three partition tables will be created.
public.emp_1_prt_year2008
public.emp_1_prt_year2009
public.emp_1_prt_year2010
We need to manually mention name to each partition. This will create partition by year and subpartition by month.
CREATE TABLE emp (id int, date_1 date, amt decimal(10,2))
DISTRIBUTED BY (id)
partition by range (date_1)
SUBPARTITION BY RANGE (date_1)
(
PARTITION year_2008 START ('2008-01-01'::date) INCLUSIVE END ('2009-01-01'::date)
(
SUBPARTITION mnth_jan START ('2008-01-01'::date) INCLUSIVE,
SUBPARTITION mnth_feb START ('2008-02-01'::date) INCLUSIVE
END ('2008-03-01'::date)
),
PARTITION year_2009 START ('2009-01-01'::date) INCLUSIVE END ('2010-01-01'::date)
(
SUBPARTITION mnth_jan START ('2009-01-01'::date) INCLUSIVE,
SUBPARTITION mnth_feb START ('2009-02-01'::date) INCLUSIVE
END ('2009-03-01'::date)
)
);
Related
I have a table which has a DATE_ID column (Integer data type). This column basically stores dates in ID format. Input data:
DATE_ID
19961210
19991001
20051212
20090108
I need to partition this table (YEARly) based on date_id column. Please note that this is an existing process and we are migrating our tables from database to another. These column datatypes cannot be changed as they will be referred by downstream process in that fashion only.
I tried below interval partitioning but somehow didn't work. Can someone pls help?
CREATE TABLE test (date_id INT NOT NULL, text VARCHAR2(500))
PARTITION BY RANGE (DATE_ID) INTERVAL (365)
(
PARTITION P0 VALUES LESS THAN (19961231),
PARTITION P1 VALUES LESS THAN (19991231),
PARTITION P2 VALUES LESS THAN (20091231)
);
I got the solution:
CREATE TABLE test (
DATE_ID INTEGER
)
partition BY RANGE (DATE_ID )
interval(10000)
(partition p0 values less than(19961231),
partition p1 values less than(19991231),
partition p1 values less than(20091231)
);
You can add a virtual column in order to add an interval partition along with a decent date type column, and able to cross-check the eligibility of the existing integer values as been considered as date values such as
CREATE TABLE test(
date_id INT NOT NULL,
text VARCHAR2(500)
);
ALTER TABLE test ADD (dt AS ( TO_DATE(date_id,'yyyymmdd') ));
ALTER TABLE test MODIFY
PARTITION BY RANGE(dt) INTERVAL (INTERVAL '1' YEAR)
(
PARTITION P1996 VALUES LESS THAN (date'1997-01-01'),
PARTITION P1999 VALUES LESS THAN (date'2000-01-01'),
PARTITION P2009 VALUES LESS THAN (date'2010-01-01')
);
if your aim is to add yearly partition as the title implies for the year range from 1996 to 2006, then rather you can prefer using a dynamic method in order to generate the desired code block such as
DECLARE
v_ddl CLOB;
BEGIN
FOR year in 1996..2006
LOOP
v_ddl := v_ddl||' PARTITION p'||year||' VALUES LESS THAN (date'''||TO_CHAR(year+1)||'-01-01''),'||CHR(13);
END LOOP;
v_ddl := 'ALTER TABLE test MODIFY PARTITION BY RANGE(dt) INTERVAL (INTERVAL ''1'' YEAR)'||CHR(13)||'('||CHR(13)||RTRIM(v_ddl,','||CHR(13));
v_ddl := v_ddl||CHR(13)||')';
DBMS_OUTPUT.PUT_LINE(v_ddl);
EXECUTE IMMEDIATE v_ddl;
END;
/
Summary of the question: To Create table with partitions which are range partitioned. However records which do not know the range value should reside in a different (default) partition and be moved to the correct partition when the value is filled. The default partition would never be dropped while the other partitions would be dropped after a defined retention period via an script.
The whole story:
I have a table where the records have to be placed in a partition based on a date field. This is a growing table and after some time the data from these partitions can be purged. I used to create table with something like the snippet below.
This works fine because we knew the value of the date column based on which we partition (RDATE). However in our new project we do not know this when a record is inserted. The value would eventually be filled in during the course of the application processing.
My initial thought was to create MAXPARTITION (MAXVALUE) which would be a catch-all partition for records which do not have the date filled and enable ROW MOVEMENTS so that when the date is filled it moves into an appropriate partition. However I think it is not possible to have both MAXVALUE partition and interval partitioning together. Is that right?
Also Is there a better way to do this?
PARTITION BY RANGE ("RDATE") INTERVAL (NUMTODSINTERVAL (1,'DAY'))
SUBPARTITION BY HASH ("RKEY")
SUBPARTITION TEMPLATE (
SUBPARTITION "SP01",
SUBPARTITION "SP02",
SUBPARTITION "SP03",
SUBPARTITION "SP04",
SUBPARTITION "SP05",
SUBPARTITION "SP06",
SUBPARTITION "SP07",
SUBPARTITION "SP08",
SUBPARTITION "SP09",
SUBPARTITION "SP10",
SUBPARTITION "SP11",
SUBPARTITION "SP12",
SUBPARTITION "SP13",
SUBPARTITION "SP14",
SUBPARTITION "SP15",
SUBPARTITION "SP16" )
(PARTITION "INITIALPARTITION" VALUES LESS THAN (TO_DATE(' 2016-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
I expect a table with default and range partitions and records to move to the range partitions from the default when a column is filled.
The column you use as partition key cannot be NULL but you can use a workaround like this:
CREATE TABLE ... (
...
RDATE DATE,
PARTITION_KEY DATE GENERATED ALWAYS AS (COALESCE(RDATE, DATE '1969-12-31'))
)
PARTITION BY RANGE (PARTITION_KEY) INTERVAL (NUMTODSINTERVAL (1,'DAY'))
...
(PARTITION INITIAL_PARTITION VALUES LESS THAN (DATE '1970-01-01'))
ENABLE ROW MOVEMENT;
If you insert a record with RDATE = NULL then it will be inserted into partition INITIAL_PARTITION. For the initial data (e.g. 1970-01-01) you must select a values whicc will never fall into the "real" date values. You could also use a date in far future, e.g.
CREATE TABLE ... (
...
RDATE DATE,
PARTITION_KEY DATE GENERATED ALWAYS AS (COALESCE(RDATE, DATE '2999-12-31'))
)
PARTITION BY RANGE (PARTITION_KEY) INTERVAL (NUMTODSINTERVAL (1,'DAY'))
...
(PARTITION INITIAL_PARTITION VALUES LESS THAN (DATE '2019-04-01'))
ENABLE ROW MOVEMENT;
-- Create DEFAULT_PARTITION
INSERT INTO ... (RDATE) VALUES (NULL);
ROLLBACK;
ALTER TABLE ... RENAME PARTITION FOR (TIMESTAMP '2999-12-31 00:00:00') TO DEFAULT_PARTITION;
I'm trying to partition my table using the ID column such that all even ID's should go in partition_1 and odd ID's should go in partition_2. The only closest thing that met my needs was virtual columns.
CREATE TABLE sales
(
id NUMBER(6) NOT NULL,
mod_id AS (MOD(id, 2))
);
PARTITION BY RANGE (mod_id)
(
PARTITION mod_id VALUES LESS THAN(1),
PARTITION mod_id VALUES LESS THAN(2),
)
Is there a better way than this?
As you are using MOD function to calculate mod_id, your column would have only 2 values (0 & 1),
In that case you can go with LIST partition as below.
CREATE TABLE sales
(
id NUMBER(6) NOT NULL,
mod_id AS (MOD(id, 2))
)
PARTITION BY LIST (mod_id)
(PARTITION mod_id0 VALUES (0),
PARTITION mod_id1 VALUES (1));
It is certain that records with odd id will to partition mod_id1 and even into mod_id0.You can verify it using below query:select * from sales partition (mod_id1);
I want to create a table where I do partition by hash on one column and subpartition by list on another column. Table creation should look like below:
CREATE TABLE testt
(
Id CHAR(3),
time DATE,
month AS (EXTRACT (MONTH FROM time))
)
PARTITION BY HASH (Id)
PARTITIONS 4
STORE IN (ts1, ts2, ts3, ts4)
SUBPARTITION BY LIST (month)
SUBPARTITION template
(
SUBPARTITION JANUARY VALUES (01),
SUBPARTITION FEBRUARY VALUES (02),
...
)
I need to maintain partition by hash for legacy reasons. I can change subpartition to Range/Hash.
But Oracle is simply not letting me create Partition by hash + subpartition by list/range/hash. I searched a lot but didn't get even one example. Now I am wondering if it is even supported or not. Can someone please let me know how to do it?
Your statement has invalid syntax, see http://docs.oracle.com/database/121/SQLRF/statements_7002.htm#CJABBBAI.
The specification of hash partition count and tablespaces should be after the subpartition templates.
CREATE TABLE testt
(
Id CHAR(3),
time DATE,
month AS (EXTRACT (MONTH FROM time))
)
PARTITION BY HASH (Id)
SUBPARTITION BY LIST (month)
SUBPARTITION template (
SUBPARTITION JANUARY VALUES (01),
SUBPARTITION FEBRUARY VALUES (02),
...
)
PARTITIONS 4
STORE IN (ts1, ts2, ts3, ts4)
Below the simplified structure of a table:
create table customer(
incident_id number,
customer_id number,
customer_name varchar2(400),
sla_id number
failure_start_date date,
failure_end_date date,
churn_flag number, -- 0 or 1
active number, -- 0 or 1
constraint pk_incident_id primary key (incident_id))
PARTITION BY LIST (active)
SUBPARTITION BY LIST (churn_flag)
SUBPARTITION TEMPLATE
( SUBPARTITION sp_churn_flag_1 VALUES (1)
, SUBPARTITION sp_churn_flag_0 VALUES (0)
)
(PARTITION sp_active_1 values (1)
, PARTITION sp_active_0 VALUES (0)
)
,
ENABLE ROW MOVEMENT COMPRESS FOR QUERY LOW;
Now I need to add additonally to the existing Composite-List-Partition an Interval-Range-Partitioning, in order to partitionate the data by month (failure_starte_date - YYYYMM). The table contains data from 200701 up to now (201511). Failure_start_date < 2013 should be partitionied into one partition for older data. All newer months should have an dedicated partition, whereas partitions for upcoming months shall be created automatically.
How can this be integrating into the already existing partitoning?
You cannot do it exactly the way you want. Partitioning strategies are limited in two relevant ways: first, composite strategies can only have two levels (you need 3) and second, interval partitioning, when used in a composite strategy must be at the top level.
Here is the closest legal thing to what you want:
CREATE TABLE matt_customer
(
incident_id NUMBER,
customer_id NUMBER,
customer_name VARCHAR2 (400),
sla_id NUMBER,
failure_start_date DATE,
failure_end_date DATE,
churn_flag VARCHAR2 (1), -- 0 or 1
active VARCHAR2 (1), -- 0 or 1
active_churn_flags VARCHAR2 (2) GENERATED ALWAYS AS (active || churn_flag) VIRTUAL,
CONSTRAINT pk_incident_id PRIMARY KEY (incident_id)
)
PARTITION BY RANGE
(failure_start_date)
INTERVAL ( NUMTOYMINTERVAL (1, 'MONTH') )
SUBPARTITION BY LIST
(active_churn_flags)
SUBPARTITION TEMPLATE (
SUBPARTITION sp_ac_00 VALUES ('00'),
SUBPARTITION sp_ac_01 VALUES ('01'),
SUBPARTITION sp_ac_10 VALUES ('10'),
SUBPARTITION sp_ac_11 VALUES ('11'))
(PARTITION customer_old VALUES LESS THAN (TO_DATE ('01-JAN-2013', 'DD-MON-YYYY')))
ENABLE ROW MOVEMENT
--COMPRESS FOR QUERY LOW;
;
This uses interval-list partitioning, and uses a virtual column to combine your active and churn_flag columns into one (I turned those columns into VARCHAR2(1) for simplicity.
To make use of partition pruning, your queries would need to be modified to select active_churn_flags = '01' for example, instead of specifying values for active and churn_flag independently.