A table TMP has 5 partitions, namely P_1, P_2,....P_5.
I need to drop some partitions of TMP; the partitions to drop are derived by another query.
Ex:
ALTER TABLE TMP DROP PARTITIONS (SELECT ... From ... //expression to get partition names )
Let's say the SELECT statement returns P_1 & P_5. The part query of the ALTER statement above doesn't work. Is there any way to drop partitions with input from a SELECT statement?
You can use dynamic sql in anonymous pl/sql block;
Begin
for i in (select part_name from ... //expression to get partition names) loop
execute immediate 'ALTER TABLE TMP DROP PARTITION ' || i.part_name;
end loop;
end;
For dropping multiple partitions on a go then;
declare
v_part varchar(1000);
Begin
select LISTAGG(partition_name, ', ') WITHIN GROUP (ORDER BY partition_name DESC)
into v_part
from ... //expression to get partition names;
execute immediate 'ALTER TABLE TMP DROP PARTITION ' || v_part;
end;
You may use the following sql to generate DML for dropping multiple table partitions.
select 'ALTER TABLE ' || TABLE_OWNER || '.' || TABLE_NAME || ' DROP PARTITION ' || '"' || PARTITION_NAME || '";' from DBA_TAB_PARTITIONS
where TABLE_NAME='%YOUR_PATTERN%'order by PARTITION_NAME;
You'll need to use dynamic SQL. Something this:
begin
for prec in (SELECT ... From ... //expression to get partition names )
loop
execute immediate 'ALTER TABLE TMP DROP PARTITION '
|| prec.partition_name;
end loop;
end;
/
Clearly you need to have complete faith that your query will return only the partitions you want to drop. Or equivalent faith in your Backup & Recover plans :)
Alternatively you can use a similar approach to generate a drop script which you can review before you run it.
You have to use pl/sql block for dropping partitions from a table with select query. Use listagg for making a comma separated list.
DECLARE
var1 varchar2(50);
BEGIN
SELECT listagg(Partition_names) into var1 from table_name//expression to get partition names ;
execute immediate 'alter table tmp drop PARTITION'||var1 ;
END;
Example on listagg
select LISTAGG(partition_name,',') within group(order by table_name) as comma_list
from ALL_TAB_PARTITIONS where TABLE_owner='OWNER' AND TABLE_NAME='TABLE_NAME'
Maybe it's could somebody help.
This script drop all partitions for all partition tables for specific schema. I use it with clear DB with METADATA, for changing started (referencial) partition.
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL ();
ALTER TABLE SCHEMA_1.TABLE_2
SET INTERVAL ();
ALTER TABLE SCHEMA_1.TABLE_3
SET INTERVAL ();
set lines 100
set heading off
spool runme.sql
select 'ALTER TABLE ' || TABLE_OWNER || '.' || TABLE_NAME || ' DROP PARTITION ' || '"' || PARTITION_NAME || '";' from DBA_TAB_PARTITIONS
where
TABLE_OWNER='SCHEMA_1'
-- and TABLE_NAME='TABLE_%'
and PARTITION_NAME LIKE 'SYS_P%'
;
#runme
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
ALTER TABLE SCHEMA_1.TABLE_3
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
Yes, the script is semi-manual, but I think it's more safety.
ALTER TABLE ... INTERVAL it's need for droping last partition.
Interval must be same that it was before
Related
i want to create a trigger insert sequence i created to apply on all primary key on tables ,
CREATE SEQUENCE HR.PRIMARY_KEY
START WITH 300
INCREMENT BY 10
MAXVALUE 99990
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
create or replace trigger increment_pk_trigger
before insert
ON schema
--FOR EACH ROW
DECLARE
CURSOR get_pk_CURSOR IS
select a.constraint_name, b.column_name, a.table_name
from user_constraints a, user_cons_columns b
where a.constraint_type = 'P' and a.constraint_name = b.constraint_name;
BEGIN
FOR V_RECORD IN get_pk_CURSOR LOOP
EXECUTE IMMEDIATE 'insert into '||V_RECORD.TABLE_NAME||' :new.V_RECORD.column_name := primary_key.nextva ';
END LOOP;
END;
my problem i cannot get what will table_name will be i tried to make it on database|schema
but not work
This is not a requirement that makes sense so you realistically can't.
It would be very weird to have a single sequence as the source for the primary key on every table-- there would end up being quite a bit of contention on that single sequence. It would be much more normal to create one sequence per table or (assuming a recent version of Oracle) to simply declare the primary key as an identity column.
If you really wanted to, you could write some code that dynamically generated triggers for every table in the schema. Something like this (assuming that every table has a single column primary key and that you really want to use the same sequence to generate the primary key on every table despite the performance impact)
begin
for pk in (select a.constraint_name, b.column_name, a.table_name
from user_constraints a, user_cons_columns b
where a.constraint_type = 'P'
and a.constraint_name = b.constraint_name)
loop
execute immediate 'create or replace trigger trg_pk_' || pk.table_name ||
' before insert on ' || pk.table_name ||
' for each row ' ||
'begin ' ||
' :new.' || pk.column_name || ' := primary_key.nextval; ' ||
'end; ';
end loop
end;
I am trying to write a Procedure which essentially drops partitions from several tables which are stored in multiple schemas. End goal is to then create a dbms scheduler which will run this procedure every day and check for partitions that hold data older than 6 months. How to add functionality of looking for partitions across multiple schemas ?
I have created a Procedure which drops a partition only from a specific table.
PROCEDURE purge_ops_log_range_parts IS
BEGIN
FOR partition_rec IN (SELECT partition_name
,high_value
FROM user_tab_partitions
WHERE table_name =
'OPSWIRE_LOG_RANGE_PARTS')
LOOP
IF SYSDATE >= add_months(to_date(substr(partition_rec.high_value
,12
,19)
,'YYYY-MM-DD HH24:MI:SS')
,6)
THEN
execute_immediate('ALTER TABLE OPS_LOG_RANGE_PARTS DROP PARTITION ' ||
partition_rec.partition_name);
END IF;
END LOOP;
END purge_ops_log_range_parts;
Output is deleting partition from a specific table only however it does not look for multiple tables in various schemas.
Use the DBA_TAB_PARTITIONS or ALL_TAB_PARTITIONS views instead of USER_TAB_PARTITIONS. The former two views contain a TABLE_OWNER (i.e. schema) column which should help you accomplish your goal.
You can then parameterize your procedure to take the owner and table names as parameters:
PROCEDURE purge_ops_log_range_parts(pinOwner IN VARCHAR2,
pinTable_name IN VARCHAR2)
IS
BEGIN
FOR partition_rec IN (SELECT partition_name
,high_value
FROM DBA_TAB_PARTITIONS
WHERE TABLE_OWNER = pinOwner AND
table_name = pinTable_name)
LOOP
IF SYSDATE >= add_months(to_date(substr(partition_rec.high_value
,12
,19)
,'YYYY-MM-DD HH24:MI:SS')
,6)
THEN
execute_immediate('ALTER TABLE ' || pinOwner || '.' ||
pinTable_name || ' DROP PARTITION ' ||
partition_rec.partition_name);
END IF;
END LOOP;
END purge_ops_log_range_parts;
Best of luck.
PROCEDURE purge_partitions
(
p_owner IN VARCHAR2
,p_name IN VARCHAR2
,p_retention_period IN NUMBER
) IS
BEGIN
FOR partition_rec IN (SELECT partition_name
,high_value
FROM dba_tab_partitions
WHERE table_owner = p_owner
AND table_name = p_name)
LOOP
IF SYSDATE >= add_months(to_date(substr(partition_rec.high_value
,12
,19)
,'YYYY-MM-DD HH24:MI:SS')
,p_retention_period)
THEN
execute_immediate('ALTER TABLE ' || p_owner || '.' ||
p_name || ' DROP PARTITION ' ||
partition_rec.partition_name)
END IF;
END LOOP;
END purge_partitions;
Purge_Partitions procedure deals with dropping partitions based on
specific retention priods mentioned in a seperate Config Table. I am
now trying to enhance this functionality which will take care of
rebuilding global indexes of those partitioned tables. Not sure how to go about this, any help is highly appreciated.
Consider the update_index_clauses, which keeps the indexes valid.
See the documentation and consideration for global indexes here
in your case it will be:
alter table ttttt drop partition pppppp update global indexes;
Alternatively let the indices be invalidated in the DROP PARTITION and rebuild them with alter index xxx rebuild. You can get the list of indexes to rebuild from this query
select OWNER, INDEX_NAME
from all_indexes
where owner = 'ooo' and table_name = 'tttttt' and status = 'UNUSABLE';
I am using an oracle database. In the database I have I have a lot of tables. The names start with "temp_....": e.g. TEMP_EXP1, TEMP_EXP2, Is it possible to drop all of them at once with one query, e.g. by specifying a regular expression?
You can't drop table with a query. You must use DROP TABLE xxxx DDL.
But you can write a query that produces the required DROPstatements, such as
select 'DROP TABLE '||owner||'.'|| table_name||';' as ddl
from dba_tables where
owner = 'xx' and
table_name <<< put here your REGEXP filter
Than copy the produced DROP statements and execute them in you IDE or SQL*Plus.
In case you have no access to the dictionary view DBA_TABLES and you drop the tables in a schema you are connected use this statement
select 'DROP TABLE '|| table_name||';' as ddl
from user_tables
where
table_name <<< put here your REGEXP filter
You can use some dynamic SQL to drop your tables; for example:
begin
for i in 1..3 loop
execute immediate 'drop table TEMP_EXP' || i;
end loop;
end;
You could loop through the tables and drop it using EXECUTE IMMEDIATE.
DBMS_OUTPUT is added for displaying the name of the table being dropped. Be careful before running!
SET SERVEROUTPUT ON
EXEC dbms_output.enable(NULL);
BEGIN
FOR t IN (SELECT owner, table_name
FROM all_tables
WHERE REGEXP_LIKE (table_name, 'TEMP_EXP\d+')) --or any other REGEX that suits you.
LOOP
DBMS_OUTPUT.PUT_LINE (
'DROPPING TABLE ' || t.owner || '.' || t.table_name);
EXECUTE IMMEDIATE 'DROP TABLE ' || t.owner || '.' || t.table_name;
END LOOP;
END;
/
I have a table ident, and I also have a table ident_hist, which just keeps a log from the table ident. The table ident gets altered a lot, so I want to add the new columns to ident_hist dynamically as well. I have created a procedure which does that:
create or replace procedure prc_create_hist_tabel(p_naam_hist_tabel in varchar2, p_naam_tabel in varchar2) is
cursor c is
select 'alter table ' || p_naam_hist_tabel || ' add ' || column_name || ' ' || data_type || case when data_type = 'DATE' then null else '(' || data_length || ')' end lijn
from user_tab_columns
where TABLE_NAME = upper(p_naam_tabel)
and column_name not in (select column_name from user_tab_columns where table_name = upper(p_naam_hist_tabel));
v_dummy number(1);
begin
begin
select 1 into v_dummy
from user_tab_columns
where TABLE_NAME = upper(p_naam_hist_tabel)
group by 1;
exception when no_data_found then
execute immediate 'create table ' || p_naam_hist_tabel || ' (wijziger varchar2(60) default user, wijzigdatum date default sysdate, constraint pk_' || p_naam_hist_tabel || ' primary key (wijziger, wijzigdatum))';
end;
for i in c
loop
execute immediate i.lijn;
end loop;
end;
My question is, how can I check in my DDL trigger if I'm altering the tabel ident?
I want to do something like this:
create or replace trigger ident_hist_trig before alter on ident
begin
prc_create_hist_tabel('ident_hist', 'ident');
end;
When I try to compile the trigger, I get this error message:
ORA-30506: system triggers cannot be based on tables or views
How can I make my DDL trigger check if I'm altering my table ident? I only want to fire the trigger it when I'm altering the table ident, not any other table.
30506, 00000, "system triggers cannot be based on tables or views"
Cause: An attempt was made to base a system trigger on a table or a
view.
Action: Make sure the type of the trigger is compatible with the base
object.
System triggers are not associated with individual objects.
You can create DDL trigger before before create or alter or drop on SCHEMA(User/Owner). Then you can filter the object names and the DDL types(DROP, ALTER).
Here Tom has explained about this in detail.
Writting DDL_EVENT Triggers