SQL> desc sales
Name Null? Type
----------------- -------- ------------
PROD_ID NOT NULL NUMBER
CUST_ID NOT NULL NUMBER
TIME_ID NOT NULL DATE
CHANNEL_ID NOT NULL NUMBER
PROMO_ID NOT NULL NUMBER
QUANTITY_SOLD NOT NULL NUMBER(10,2)
AMOUNT_SOLD NOT NULL NUMBER(10,2)
1) First we select a partitioning expression.
TO_CHAR(time_id,’YYYY_Q’)
2) Next we create as many tables as partitions we need:
-- -----------------------------------------------------------
-- Create first partition:
--
CREATE TABLE sales_part_2010_1
(
trans_id NUMBER NOT NULL,
prod_id NUMBER NOT NULL,
cust_id NUMBER NOT NULL,
time_id DATE NOT NULL,
channel_id NUMBER,
promo_id NUMBER,
quantity_sold NUMBER( 10, 2 ) NOT NULL,
amount_sold NUMBER( 10, 2 ) NOT NULL
);
--
-- Create the other partitions:
--
CREATE TABLE sales_part_2010_2 AS
SELECT * FROM sales_part_2010_1 WHERE 1 = 0;
CREATE TABLE sales_part_2010_3 AS
SELECT * FROM sales_part_2010_1 WHERE 1 = 0;
-- Etc ...
CREATE TABLE sales_part_2012_4 AS
SELECT * FROM sales_part_2010_1 WHERE 1 = 0;
3) Perhaps add the primary key and some indexes to each of the tables:
-- -----------------------------------------------------------
-- Create local primary keys:
-- -----------------------------------------------------------
CREATE UNIQUE INDEX sales_part_2010_1_pk
ON sales_part_2010_1( trans_id );
ALTER TABLE sales_part_2010_1 ADD (
CONSTRAINT sales_part_2010_1_pk PRIMARY KEY (trans_id) USING INDEX sales_part_2010_1_pk ENABLE VALIDATE);
-- Etc ...
CREATE UNIQUE INDEX sales_part_2012_4_pk
ON sales_part_2012_4( trans_id );
ALTER TABLE sales_part_2012_4 ADD (
CONSTRAINT sales_part_2012_4_pk PRIMARY KEY (trans_id) USING INDEX sales_part_2012_4_pk ENABLE VALIDATE);
-- -----------------------------------------------------------
-- Create some other indexes:
--
CREATE INDEX sales_part_2010_1_idx1
ON sales_part_2010_1( time_id, prod_id, cust_id );
CREATE INDEX sales_part_2010_2_idx1
ON sales_part_2010_2( time_id, prod_id, cust_id );
-- Etc ...
CREATE INDEX sales_part_2012_4_idx1
ON sales_part_2012_4( time_id, prod_id, cust_id );
4) And now, the trick is to create a view to group these tables by using the union set operator:
CREATE OR REPLACE VIEW sales_part AS
SELECT * FROM sales_part_2010_1 UNION ALL
SELECT * FROM sales_part_2010_2 UNION ALL
SELECT * FROM sales_part_2010_3 UNION ALL
SELECT * FROM sales_part_2010_4 UNION ALL
SELECT * FROM sales_part_2011_1 UNION ALL
SELECT * FROM sales_part_2011_2 UNION ALL
SELECT * FROM sales_part_2011_3 UNION ALL
SELECT * FROM sales_part_2011_4 UNION ALL
SELECT * FROM sales_part_2012_1 UNION ALL
SELECT * FROM sales_part_2012_2 UNION ALL
SELECT * FROM sales_part_2012_3 UNION ALL
SELECT * FROM sales_part_2012_4;
5) Create an index organized table to preserve/manage unique keys:
CREATE TABLE sales_part_uniq_keys
(
trans_id NUMBER PRIMARY KEY NOT NULL,
time_id DATE NOT NULL,
cust_id NUMBER NOT NULL,
prod_id NUMBER NOT NULL,
channel_id NUMBER NOT NULL
)
ORGANIZATION INDEX INCLUDING channel_id
PCTTHRESHOLD 2 STORAGE( INITIAL 4 K )
OVERFLOW STORAGE( INITIAL 4 K );
--
-- Create the unique key(s):
--
CREATE UNIQUE INDEX sales_part_uk1
ON sales_part_uniq_keys( time_id,
cust_id,
prod_id,
channel_id );
6) In order to apply dml operations to these tables, we will be creating ‘INSTEAD OF’ triggers on the view:
-- -----------------------------------------------------------
-- Insert trigger
--
CREATE OR REPLACE TRIGGER sales_part_insert
INSTEAD OF INSERT
ON sales_part
FOR EACH ROW
DECLARE
v_sql VARCHAR2( 4096 );
BEGIN
--
-- Section to manage unique keys:
--
INSERT INTO sales_part_uniq_keys
VALUES ( :new.trans_id,
:new.time_id,
:new.cust_id,
:new.prod_id,
:new.channel_id );
--
-- Section to manage insert:
--
v_sql :=
'INSERT INTO sales_part_'|| TO_CHAR( :new.time_id, 'YYYY_Q' )
|| ' VALUES(:1,:2,:3,:4,:5,:6,:7,:8) ';
EXECUTE IMMEDIATE v_sql
USING :new.trans_id,
:new.prod_id,
:new.cust_id,
:new.time_id,
:new.channel_id,
:new.promo_id,
:new.quantity_sold,
:new.amount_sold;
END;
/
-- -----------------------------------------------------------
-- Delete trigger
--
CREATE OR REPLACE TRIGGER sales_part_delete
INSTEAD OF DELETE
ON sales_part
FOR EACH ROW
DECLARE
v_sql VARCHAR2( 4096 );
BEGIN
--
-- Section to manage delete:
--
v_sql :=
'DELETE FROM sales_part_'|| TO_CHAR( :old.time_id, 'YYYY_Q' )
|| ' WHERE TRANS_ID = :1';
EXECUTE IMMEDIATE v_sql USING :old.trans_id;
--
-- Section to manage unique keys:
--
DELETE FROM sales_part_uniq_keys
WHERE TRANS_ID = :old.trans_id;
END;
/
-- -----------------------------------------------------------
-- Update trigger
--
CREATE OR REPLACE TRIGGER sales_part_update
INSTEAD OF UPDATE
ON sales_part
FOR EACH ROW
DECLARE
v_sql VARCHAR2( 4096 );
BEGIN
--
-- Section to manage unique keys:
--
IF :old.trans_id <> :new.trans_id
OR :old.time_id <> :new.time_id
OR :old.cust_id <> :new.cust_id
OR :old.prod_id <> :new.prod_id
OR :old.channel_id <> :new.channel_id
THEN
DELETE FROM sales_part_uniq_keys
WHERE TRANS_ID = :old.trans_id;
INSERT INTO sales_part_uniq_keys
VALUES ( :new.trans_id,
:new.time_id,
:new.cust_id,
:new.prod_id,
:new.channel_id );
END IF;
--
-- Section to manage update:
--
IF :old.trans_id <> :new.trans_id
OR :old.time_id <> :new.time_id
THEN
v_sql :=
'DELETE FROM sales_part_'|| TO_CHAR( :old.time_id, 'YYYY_Q' )
|| ' WHERE TRANS_ID = :1';
EXECUTE IMMEDIATE v_sql USING :old.trans_id;
v_sql :=
'INSERT INTO sales_part_'|| TO_CHAR( :new.time_id, 'YYYY_Q' )
|| ' VALUES(:1,:2,:3,:4,:5,:6,:7,:8) ';
EXECUTE IMMEDIATE v_sql
USING :new.trans_id,
:new.prod_id,
:new.cust_id,
:new.time_id,
:new.channel_id,
:new.promo_id,
:new.quantity_sold,
:new.amount_sold;
ELSE
v_sql :=
'UPDATE sales_part_'|| TO_CHAR( :new.time_id, 'YYYY_Q' )
|| ' SET prod_id = :1'
|| ' , cust_id = :2'
|| ' , time_id = :3'
|| ' , channel_id = :4'
|| ' , promo_id = :5'
|| ' , quantity_sold = :6'
|| ' , amount_sold = :7'
|| ' WHERE trans_id = '||:old.trans_id;
EXECUTE IMMEDIATE v_sql
USING :new.prod_id,
:new.cust_id,
:new.time_id,
:new.channel_id,
:new.promo_id,
:new.quantity_sold,
:new.amount_sold;
END IF;
END;
/
7) And finally populate our partitions from the SH.SALES table:
INSERT INTO sales_part
SELECT ROW_NUMBER() OVER (ORDER BY time_id, cust_id),
prod_id,
cust_id,
ADD_MONTHS( time_id, 125 ) time_id,
channel_id,
promo_id,
quantity_sold,
amount_sold
FROM sh.sales
WHERE ADD_MONTHS( time_id, 125 ) >= TO_DATE( '20100101', 'yyyymmdd' );
COMMIT;
Pro’s
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (4)
Author
Commented:I already have prepared full working example, I will correct article and post the code.
Author
Commented:The script is ready and it works, to test:
1) Grant "scott" create view and perhaps "resource".
2) Gran select on sh.sales to scott
3) Connect as "scott" and execute the script.
Regards,
MikeOM>
Author
Commented:Because they are a composite unique key (for this example):
Open in new window
The idea being that any unique key be added to the uniq_keys table.Commented: