Oracle | drop table - sql

I want to drop certain tables in a tablespace that has common name appended to end of each table for an example:
TABLE1_NAME1_COMMON
TABLE2_NAME2_COMMON
TABLE3_NAME3_COMMON
I heard about Oracle functions but I'm not familiar much with those so I'm expecting some helping hand.
Thanks.

If you're completely sure what you're doing, ie, if you're sure that you don't accidentally drop a table that you don't want to drop, you can do a:
set serveroutput on size 1000000
begin
for r in (
select table_name
from user_tables
where table_name like '%\_COMMON' escape '\')
loop
execute immediate 'drop table ' || r.table_name;
end loop;
exception when others then
dbms_output.put_line(sqlerrm);
end;
/
Edit:
Changed Now selecting from user_tables instead of dba_tables as it seems more safe to do.
Added set serveroutput on in order for dbms_output.put_line to be printed
Added begin .. exception .. end in order for errors to be shown.

You could do that in a procedure, but it might be better to just select those DROP-statements, review them and execute them manually:
SELECT 'DROP TABLE ' || table_name || ';'
FROM user_tables
WHERE table_name LIKE '%\_COMMON' ESCAPE '\';
would return
DROP TABLE TABLE1_NAME1_COMMON;
DROP TABLE TABLE2_NAME2_COMMON;
DROP TABLE TABLE3_NAME3_COMMON;

to identify them you can use:
SELECT * FROM user_tables WHERE tablespace_name='MySpace' AND table_name like '%COMMON';
You could then either, derive your DROP statements using a SELECT. Or you could write a PL/SQL function to loop through the "Common Tables" and DROP them using EXECUTE IMMEDIATE.
I would make sure you are 100% sure in the selections first however.

Related

Oracle stored procedure: FOR LOOP over a column from a variable

I want to save a list of column values into the variable input_cols and then loop over the values
CREATE OR REPLACE PROCEDURE map_values (mapping_table VARCHAR2(64)) AS
TYPE col_names IS TABLE OF VARCHAR2(64);
input_cols col_names;
BEGIN
EXECUTE IMMEDIATE
'SELECT COLUMN_NAME FROM SYS.DBA_TAB_COLUMNS
WHERE TABLE_NAME = '' ' || mapping_table || ' '' '
BULK COLLECT INTO (input_cols);
FOR in_col IN input_cols
LOOP
dbms_output.put_line ('test');
END LOOP;
END;
I am getting the error
PLS-00103: Encountered the symbol "LOOP" when expecting one of the following: * & - + / at mod remainder rem .. <an exponent (**)> ||
Although you can construct dynamic queries by concatenating values, it's generally better to use bind variables where possible, for example:
execute immediate
'select column_name from user_tab_columns where table_name = :b1'
bulk collect into input_cols
using p_table;
I recommend getting into the habit of anchoring types in your code to the corresponding database object, when there is one. For example, this:
mapping_table dba_tab_columns.table_name%type
instructs the compiler to look up the type of dba_tab_columns.table_name and use that. However, I would generally avoid the dba_ views in procedures like this and stick to user_ views, e.g. user_tab_columns, to limit them to objects you own. If you must use dba_ views, you should also include the table owner, as there may be more than one table with the same name.
I also prefer to name my parameters in a way that separates them from column names etc. There are various conventions (camelCase, prefixing with i_ for in or p_ for parameter, prefixing with the procedure name e.g. map_values.mapping_table), so pick one you like.
Putting that together, you get something like this:
create or replace procedure map_values
( p_table user_tab_columns.table_name%type )
as
type col_names is table of user_tab_columns.column_name%type;
input_cols col_names;
begin
execute immediate
'select column_name from user_tab_columns where table_name = :b1 order by column_id'
bulk collect into input_cols
using p_table;
for i in 1..input_cols.count loop
dbms_output.put_line(input_cols(i));
end loop;
end map_values;
Or, if you don't specifically need a collection and just want to loop through a result set:
create or replace procedure map_values
( p_table user_tab_columns.column_name%type )
as
columns_cur sys_refcursor;
colname user_tab_columns.column_name%type;
begin
open columns_cur for
'select column_name from user_tab_columns where table_name = :b1 order by column_id'
using p_table;
loop
fetch columns_cur into colname;
exit when columns_cur%notfound;
dbms_output.put_line(colname);
end loop;
close columns_cur;
end;
As Koen pointed out in the comments, though, there is no need for dynamic SQL in this example, so a much simpler version could be just:
create or replace procedure map_values
( p_table user_tab_columns.column_name%type )
as
begin
for r in (
select column_name from user_tab_columns
where table_name = p_table
order by column_id
)
loop
dbms_output.put_line(r.column_name);
end loop;
end map_values;
Word of advice: use a tool like SQL Developer to create your procedures. They show the compilation errors in a much clearer way. If you're new to PL/SQL, start with the very basics (empty procedure), compile, fix error if any and add code. There are 3 blocking issues in your code - debugging that is pretty hard.
I added a comment for each of the errors
create or replace PROCEDURE map_values
(mapping_table VARCHAR /* just define the datatype, not the precision */
)
AS
TYPE col_names IS TABLE OF VARCHAR2(64) INDEX BY BINARY_INTEGER;
input_cols col_names;
BEGIN
EXECUTE IMMEDIATE
'SELECT COLUMN_NAME FROM SYS.DBA_TAB_COLUMNS
WHERE TABLE_NAME = ''' ||mapping_table|| ''' '
BULK COLLECT INTO input_cols; /* no brackets needed */
dbms_output.put_line ('test:');
FOR in_col IN 1 .. input_cols.COUNT /* this is not a implicit cursor but a collection - you need to iterate over it.*/
LOOP
dbms_output.put_line ('test:'||input_cols(in_col));
END LOOP;
END;
/
Please do not use dynamic SQL for this use case, static SQL is sufficient and will be more efficient.
For looping, you have to use indexes and get the element from collection by index. There is no functionality that will enable you to directly assign collection element to a variable (like you are trying).
create or replace procedure map_values(mapping_table sys.dba_tab_columns%table_name)
as
type col_names is table of sys.dba_tab_columns.column_name%type;
input_cols col_names;
begin
select column_name
bulk collect into input_cols
from sys.dba_tab_columns
where table_name = mapping_table;
for i in 1 .. input_cols.count
loop
dbms_output.put_line(input_cols(i));
end loop;
end;

How to find to which schema or procedure, table and column a particular value belongs to?

I have a 9 digit number, say "234234234", is there a way to find or check its appearance in my database, like in which particular schema or procedure does it fall? and list out all the tables and columns which has that value in pl/sql developer
This query only searches in stored objects that user is allowed to access (procedure, function, package, ...). You could refer to this
Not sure if there is one way to search for that value in all database table.
SELECT *
FROM all_source
WHERE text LIKE '%234234234%';
--AND owner = 'SCHEMA_NAME';
The below block identifies the given string's presence across all the tables in your DB.
declare
num_rows number;
sql_text varchar2(250);
sql_info varchar2(100);
begin
dbms_output.enable(1000000);
for x in (select table_name, column_name from dba_tab_columns
where data_type in ('VARCHAR','VARCHAR2','CHAR')
and owner <> 'SYSTEM')
loop
sql_text:='select count(*) into :num_rows from SYSTEM.'||x.table_name||' where '||x.column_name||' like ''%234234234%''';
-- dbms_output.put_line (sql_text);
execute immediate sql_text into num_rows;
if num_rows>0
then
sql_info:='Table: '||x.table_name||' contains the string';
dbms_output.put_line (sql_info);
end if;
end loop;
end;
/

ORACLE SQL: Looping over table

I have a table that contains a list of table names.
I would like to search each of these tables one by one to see if they contain a particular element (the primary key, specified at the start of the script).
I would like to return a list of all of the tables that this element is present in (ideally distinct).
I'm fairly new to this PL/SQL "not just a query" stuff. so i apologise in advance for the attrocious attempt you are about to see, but hopefully it illustrates what i'm going for:
PROCEDURE CHECK_FOR_ELEMENTS
BEGIN
DECLARE
ELEMENT_KEY varchar(5):=X78ehryfk;
RNUM_MAX int :=167;
----create output table for script
create or replace table ALL_TABLES CONTAINING_&ELEMENT_KEY
(ELEMENT_KEY VARCHAR(255),
TABLE_NAME varchar(255))
/
commit;
---begin loop over rnum;
FOR rnum_counter in 1..&RNUM_MAX
LOOP
--define this statement as variable TABLE_NAME_VAR
select table_name from (select * from (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
where rnum = rnum_counter
)INTO TABLE_NAME_VAR
;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES CONTAINING_&ELEMENT_KEY
SELECT distinct key,'||TABLE_NAME_VAR||' as UMF from
'||TABLE_NAME_VAR||
' where key like 'ELEMENT_KEY-%'
execute immediate SQL_STMT;
commit;
---insert row into table created for output
END LOOP
---loop over all tables
END;
The main error message i get is that TABLE_NAME_VAR is not a valid table name within the dynamic SQL statement. I've googled a bit and i now understand you can't use variables to input table names in this way.
Any help is greatly appreciated!
Thankyou!
Here, I tried to clean it up for you. Let me know if you still get errors.
create or replace PROCEDURE CHECK_FOR_ELEMENTS is
ELEMENT_KEY varchar2(14):='X78ehryfk';
RNUM_MAX int :=167;
TABLE_NAME_VAR varchar2(30);
SQL_STMT varchar2(4000);
BEGIN
----create output table for script
begin
execute immediate 'drop table ALL_TABLES_WITH_' || element_key;
exception when others then null;
end;
execute immediate 'create table ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY VARCHAR2(255), -- does this need to be 255 characters?
TABLE_NAME varchar2(30))';
--- implicit cursor loop
FOR rnum_row in (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
LOOP
if rnum_row.rnum > RNUM_MAX
then exit;
end if;
TABLE_NAME_VAR := rnum_row.table_name;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY, TABLE_NAME)
SELECT distinct key, :1 as UMF from
'||TABLE_NAME_VAR||
' where key like :2';
execute immediate SQL_STMT using TABLE_NAME_VAR, element_key || '-%';
---insert row into table created for output
END LOOP;
commit;
---loop over all tables
END CHECK_FOR_ELEMENTS;
/

Get all ids in a database

I want to get all the ids in a database from all columns that have an ID field.
My script so far is:
BEGIN
FOR tname IN (select table_name from all_tab_columns where column_name = 'ID' and owner='PACC_USER') LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname;
END LOOP;
End;
I get the error PLS-00306: wrong number or types of arguments in call to '||'. What is the problem exactly? Any help is welcomed :)
In your code tname is a record for referencing the cursor result set i.e. a namespace not an attribute. Fix it like this:
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name ;
END LOOP;
End;
One would hope that a column called ID would return unique rows without needing the unique keyword but we live in troubled times.
Your code needs to select results into something: PL/SQL is not T-SQL, it requires target variables. So let's improve your code a bit more.
declare
ids_nt sys.dbms-debug_vc2coll;
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name
bulk collect into ids_nt;
dbms_output.put_line('IDS for table '|| tname.table_name);
for idx in ids_nt.first() .. ids_nt.last loop
dbms_output.put_line(ids_nt(idx));
end loop;
END LOOP;
End;
Maybe this isn't the kind of thing you want to do with the IDs. If so please edit your question to clarify your intent.
Select column_name,table name from all_tab_column where upper(column_name)=upper('Id') ;

Drop table having name: <table_name>_(sysdate-1) only if the table exist

I need to drop a table only if the table exist, can do it using plsql block
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE <table_name>;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END
But in my case the table name has sysdate (02062017) eg table001_02072017 and i need to delete all such tables with sysdate-1.
How can i do so?
You can find the table with the given pattern from user_tables dictionary table and loop on the result to drop each one by one.
begin
for t in (select table_name from user_tables
where table_name like '%\_'||to_char(sysdate-1,'mmddyyyy') escape '\')
loop
execute immediate 'drop table ' || t.table_name;
end loop;
exception
/* Handle your exceptions here. */
end;
/
Using WHEN OTHERS in your exception handling is discouraged. You should explicitly handle the errors.
Dynamic sql will help you here just use
execute immediate 'drop table ' || table_name;
inside your procedure