Oracle SQL: repeating a process for multiple tables - sql

I have Oracle 11g. I would like to 'loop' this code through multiple tables t1 is already there, but I need to do this for 4 tables (t1,t2,t3, and t4):
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE t1';
EXECUTE IMMEDIATE 'DROP TABLE t1';
EXCEPTION WHEN OTHERS THEN
IF SQLCODE !=-942 THEN
RAISE;
END IF;
END;
I can change this manually, but the code then swells and I would really like to control it just once.:-) Also the tables may not be sequentially labelled. Thank you in advance.
Kate

Make a loop where you select the table names from dual. You could also have a table containing the names and select from this.
BEGIN
for t in
(
select 't1' as table_name from dual
union all
select 't2' as table_name from dual
union all
select 't3' as table_name from dual
union all
select 't4' as table_name from dual
)
loop
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || t.table_name;
EXECUTE IMMEDIATE 'DROP TABLE ' || t.table_name;
end loop;
EXCEPTION WHEN OTHERS THEN
IF SQLCODE !=-942 THEN
RAISE;
END IF;
END;

There is no need to truncate a table before you drop it, so the drop table is sufficient. Additionally, I'd add the purge modifier so that it doesn't clutter the recycle bin.
begin
for t in (select table_name from user_tables
where regexp_like(table_name, '^T[1-4]$')) loop
execute immediate 'drop table ' || t.table_name || ' purge';
end loop;
end;
/
Of course, you can drop the table in a specific procedure:
declare
procedure drop_table(t in varchar2) is begin
execute immediate 'drop table ' || t || ' purge';
exception when others then
if sqlcode != -942 then
raise;
end if;
end drop_table;
begin
drop_table('T1');
drop_table('T2');
drop_table('T3');
drop_table('T4');
end;
/

Related

configuring the oracle procedure so that it should accept the input value

I have created an procedure which delete the 3 months old partition from the table now i want to make the below procedure configurable enough such that it should accept the count from the user so please advise how can i modify the below procedure
create or replace
PROCEDURE Delete_partitions
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', 2) THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/
Add some parameters to it:
create or replace
PROCEDURE Delete_partitions(cnt in number)
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', ' || cnt-1 || ') THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/

checking the validity of a stored procedure statement

I am trying to execute this stored procedure statement,
CREATE OR REPLACE PROCEDURE table_records_select IS
TYPE loc_array_type IS TABLE OF VARCHAR2(100)
INDEX BY binary_integer;
dml_str VARCHAR2 (200);
loc_array loc_array_type;
BEGIN
-- bulk fetch the list of tables
SELECT table_name BULK COLLECT INTO loc_array
FROM all_tab_columns;
-- for each table, delete the records where EXCN_ID matches with EXCN_ID of t_int_excn_log table where excn_strt_tm < sysdate-7
FOR i IN loc_array.first..loc_array.last LOOP
dml_str := 'select B.* from t_int_excn_log A,'
|| loc_array(i) || ' B'
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
EXECUTE IMMEDIATE dml_str ;
END LOOP;
END;
/
SHOW ERRORS;
It seems that stored procedure is created successfully and showing valid.
But when I try to execute it, It shows various errors,
ORA-00933: SQL command not properly ended
ORA-06512: at "HIAB_UAT.TABLE_RECORDS_SELECT", line 19
ORA-06512: at line 3
In the end it says 'Procedure Completed'
Can anyone help me on this.
There is one mistake in your Dynamic SQL Preparation: at second line of "dml_str" preparation. Space missed just after alias "B".
Your Code:
|| loc_array(i) || ' B'
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
It should be:
|| loc_array(i) || ' B '
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
CREATE OR REPLACE PROCEDURE table_records_select IS
TYPE loc_array_type IS TABLE OF VARCHAR2(100)
INDEX BY binary_integer;
dml_str VARCHAR2 (200);
loc_array loc_array_type;
BEGIN
-- bulk fetch the list of tables
SELECT table_name BULK COLLECT INTO loc_array
FROM user_tab_columns;--all_tab_columns;
-- for each table, delete the records where EXCN_ID matches with EXCN_ID of t_int_excn_log table where excn_strt_tm < sysdate-7
FOR i IN loc_array.first..loc_array.last LOOP
dml_str := 'select B.* from t_int_excn_log A,'
|| loc_array(i) || ' B '
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
EXECUTE IMMEDIATE dml_str ;
END LOOP;
END;

DROP all tables starting with "EXT_" in Oracle SQL

I know this question may ask many times but I could not find one line SQL statement.
I remember I did it before but now I could not remember how I did
I want to drop all tables whose name starts with "EXT_". Is it possibile to make it happen with one line SQL statement.
You could use a short anonymous block to do this.
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT_%' )
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
It's not possible with only one statement. Usually, I write a sql to get all the tables and then execute the results:
select 'drop table ' || table_name || ';'
from user_tables
where table_name like 'EXT_%';
This code will DROP not only EXT_% tables, it will act as DROP EXT% also.
Underscore is as special wildcard that acts as '%' but for a single character.
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT_%' )
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
In order to achieved desired results you should change your code the way below
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT\_%' ESCAPE '\')
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
It escapes underscore char in order to be trated literally, ESCAPE '\' modifier indicates that escape char is '\'
In most of cases you will find contraints violations. In that case, this script can help you:
DECLARE
c_action CONSTANT VARCHAR2(10) := 'DROP';
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'STARTINGTEXT_%' )
LOOP
FOR reg IN (SELECT uc.table_name,
uc.constraint_name
FROM user_constraints uc
WHERE uc.table_name IN (c.table_name)) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || reg.table_name || ' ' || c_action ||
' CONSTRAINT ' || reg.constraint_name ;
END LOOP;
END LOOP;
COMMIT;
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'STARTINGTEXT_%' )
LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || c.table_name;
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;

Best way to maintain daywise interval partitions in 11g

I had created a sql script in 11g that would drop all partitions having high value less than 60 days in all partitioned tables in the database
DECLARE
TNAME VARCHAR2 (300);
PNAME VARCHAR2 (300);
HIGHVAL VARCHAR2 (3000);
POSITION SMALLINT;
VAL LONG;
CURSOR C1
IS
SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, HIGH_VALUE
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME NOT LIKE '%$%'
AND TABLE_NAME NOT LIKE 'BIN%';
BEGIN
OPEN C1;
LOOP
FETCH C1
INTO TNAME, PNAME, POSITION, VAL;
HIGHVAL := VAL;
EXIT WHEN C1%NOTFOUND;
IF TO_DATE (SUBSTR (HIGHVAL, 10, 11), 'RRRR-MM-DD') <
TRUNC (SYSDATE)
- 60
THEN
IF POSITION = 1
THEN
DBMS_OUTPUT.PUT_LINE ('ALTER TABLE ' || TNAME
|| ' SET INTERVAL();'
);
END IF;
DBMS_OUTPUT.PUT_LINE ( 'ALTER TABLE '
|| TNAME
|| ' DROP PARTITION '
|| PNAME
|| ' UPDATE GLOBAL INDEXES PARALLEL 2;'||CHR(10)
|| '--DROPPED'
|| '--'
|| TO_DATE (SUBSTR (HIGHVAL, 10, 11),
'RRRR-MM-DD'
)
);
IF POSITION = 1
THEN
DBMS_OUTPUT.PUT_LINE
( 'ALTER TABLE '
|| TNAME
|| ' SET INTERVAL(NUMTODSINTERVAL(1,''DAY''));'
);
END IF;
END IF;
END LOOP;
COMMIT;
CLOSE C1;
END;
/
I'm executing the generated SQL text
Kindly advice if this correct and any room for enhancement???
A few hints:
It's a perfect place for implicit cursor, so you don't need to manually declare variables, fetch, open & close, ...
Filter high value in the query, earlier is better
Use EXECUTE IMMEDIATE to apply changes instead of manually executing whatever gets printed
You don't need to commit, since it's all DDL
The code:
BEGIN
FOR p IN (SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, HIGH_VALUE
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME NOT LIKE '%$%'
AND TABLE_NAME NOT LIKE 'BIN%'
AND TO_DATE (SUBSTR (HIGH_VALUE, 10, 11), 'RRRR-MM-DD') < TRUNC (SYSDATE) - 60)
LOOP
IF p.PARTITION_POSITION = 1
THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME || ' SET INTERVAL()';
END IF;
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME
|| 'DROP PARTITION ' || p.PARTITION_NAME
|| ' UPDATE GLOBAL INDEXES PARALLEL 2';
IF p.PARTITION_POSITION = 1
THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME
|| ' SET INTERVAL(NUMTODSINTERVAL(1,''DAY''));';
END IF;
END LOOP;
END;
/
And a few warnings:
Watch out for UPDATE GLOBAL INDEXES, it does not work with IOT tables (if you use them) (I was wrong with this one)
Watch out for resetting/setting intervals. The general rule is that you can't drop last non-interval partition. Guessing this would be position 1 is risky. Better to rely on user_tab_partitions.interval flag.

Oracle Nested cursors

I want to get the distinct dates in a column called "YMDH" from each table in a schema where that column exists. I figured that I needed to use nested cursors (something I've not done before) and came up with the following code:
CREATE OR REPLACE PROCEDURE DistinctDates AS
sql_statement1 varchar2(200);
sql_statement2 varchar2(200);
results varchar2(15);
ColumnExist integer;
BEGIN
for cursor_rec in (SELECT * FROM user_objects WHERE object_type='TABLE'
AND object_name NOT LIKE 'TM%') loop
sql_statement1 := 'select count (*) from user_tab_columns where table_name=' || '''' || cursor_rec.object_name || '''' || ' and column_name=' || '''' ||'YMDH' || '''';
execute immediate sql_statement1 into ColumnExist;
if ColumnExist = 1 then
for inner_cursor_rec in (select distinct(ymdh) from cursor_rec.object_name) loop
null;
end loop;
end if;
end loop;
END DistinctDates;
SQL Developer is complaining about the select statement for the inner cursor. The error message is:
Error(18,32): PL/SQL: SQL Statement ignored
Error(18,70): PL/SQL: ORA-00942: table or view does not exist
So it's not recognizing the reference to the outer cursor. How do I pass the table name (which is the cursor_rec.object_name) to the inner cursor?
You have used dynamic SQL where it is not needed, and have not used it where it is needed!
The check to see if the table has a column called 'YMDH' can be incorporated into the first query, giving this code:
CREATE OR REPLACE PROCEDURE DistinctDates AS
sql_statement varchar2(200);
rc sys_refcursor;
ymdh_value ????; -- Appropriate data type
BEGIN
for cursor_rec in (SELECT t.table_name
FROM user_tables t
JOIN user_tab_columns c ON c.table_name = t.table_name
WHERE t.table_name NOT LIKE 'TM%'
AND c.column_name='YMDH')
loop
sql_statement := 'select distinct(ymdh) from ' || cursor_rec.table_name;
open rc for sql_statement;
loop
fetch rc into ymdh_value;
exit when rc%notfound;
null;
end loop;
close rc;
end loop;
END DistinctDates;