I need to drop an Oracle table only if it 1) exists AND 2) Is NOT Empty
I wrote this code but if the table does not exist the code does not work:
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
END;
/
What am I doing wrong? Please help.
Many thanks in advance
If you want to drop a table if it exists and empty(as the title of the question states) you could do this as follows:
create or replace procedure DropTableIfEmpty(p_tab_name varchar2)
is
l_tab_not_exists exception;
pragma exception_init(l_tab_not_exists, -942);
l_is_empty number;
l_query varchar2(1000);
l_table_name varchar2(32);
begin
l_table_name := dbms_assert.simple_sql_name(p_tab_name);
l_query := 'select count(*)
from ' || l_table_name ||
' where rownum = 1';
execute immediate l_query
into l_is_empty;
if l_is_empty = 0
then
execute immediate 'drop table ' || l_table_name;
dbms_output.put_line('Table "'|| p_tab_name ||'" has been dropped');
else
dbms_output.put_line('Table "'|| p_tab_name ||'" exists and is not empty');
end if;
exception
when l_tab_not_exists
then dbms_output.put_line('Table "'|| p_tab_name ||'" does not exist');
end;
When you are trying to drop a table, or query a table, which does not exist, Oracle will raise ORA-00942 exception and execution of a pl/sql block halts. We use pragma exception_init statement to associate ORA-00942 exception with our locally defined exception l_tab_not_exists in order to handle it appropriately.
Test case:
SQL> exec droptableifempty('tb_test'); -- tb_test table does not exists
Table "tb_test" does not exist
SQL> create table tb_test(
2 col number
3 );
table TB_TEST created.
SQL> exec droptableifempty('tb_test');
Table "tb_test" has been dropped
As a side note. Before querying num_rows column of [dba][all][user]_tables in order to determine number of rows a table has, you need to gather table statistic by executing dbms_stats.gather_table_stats(user, '<<table_name>>');, otherwise you wont get the actual number of rows.
In PL/SQL it is 'normal' to catch the exception.
If it is the correct exception then continue with the next part of your code.
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('OH DEAR AN EXCEPTION WAS THROWN DUE TO' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('THE ORACLE CODE IS ' || SQLCODE);
-- if it is the oracle code for no such table, or no data selected
-- everything is fine.
END;
Of course it won't work if the table doesn't exist. Your second select would get a "No data found" exception, and you're not doing any exception handling. At least you should move the second select inside the first IF block. Best to add exception handling.
here is an easy way to solve this problem:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE [sssss]';
EXCEPTION WHEN OTHERS THEN NULL;
END;
Related
I have some problem in execute immediate insert statement exception part.
I have a table query_tb that contains two columns (DEPT and SOURCE_VALUE)
The column contains data in below
CLERK
select a.empno,a.ename,a.job,a.mgr,a.hiredate,b.deptno,b.dname,b.loc
from emp a,dept b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
SALESMAN
select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e,dept
d where e.deptno=d.deptno and e.empno= '#V_GCIF#'
MANAGER
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
ADMIN
select a.empno,a.ename,a.job,b.deptno,b.dname,b.loc from employee
a,department b where a.deptno=b.deptno and a.empno= '#V_GCIF#'
If I pass the correct empno which is keep on the emp table it runs fine. But if I pass the incorrect empno (no data) then exception part not working.
create or replace
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
CURSOR rec
IS
SELECT dept,
source_value
FROM query_tb;
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
END IF;
END LOOP;
commit;
EXCEPTION
WHEN others THEN
dbms_output.put_line('No data Found...');
END;
That's because you are not running a select command, it is an insert command (insert select) which means that if the select won't return rows it just doesn't insert anything and no error is thrown for that. You should check whether the insert command has affected any rows. The way you do that in Oracle is checking the SQL%ROWCOUNT immediate after the execution, if it turns to be 0 then you do your job raising an exception. It would be something like:
DECLARE
customException EXCEPTION;
PRAGMA EXCEPTION_INIT( customException, -20001 );
....
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc)
('||v_query|| ')';
IF (SQL%ROWCOUNT) = 0 then
raise_application_error( -20001, 'This is a custom error' );
end if;
EXCEPTION
WHEN customException THEN
dbms_output.put_line('No data Found...');
END;
It's a long time without programming in Oracle PLSql So somethings there on the provided code may not compile but it is all there look into it in the internet and you will be good.
Exception does not work because there is no exception.
If SELECT returns NULL then 0 rows will be INSERT.
Example:
insert into t1(c1)
select 100 from dual where 1=0;
Result: 0 rows inserted.
insert into ... select from..
will not raise a no_data_found exception. It will just insert 0 records.
To test if you have inserted any records you can use SQL%ROWCOUNT after the insert statement.
execute immedate 'INSERT...;
if SQL%ROWCOUNT=0
then
dbms_output.put_line('no records inserted');
else
...
end if;
Also you might want to consider changing #V_GCIF# into something that can be used as a bind varaiable such as :P_ID.
You can skip the replace statement and change the execute immediate into something like:
execute immediate 'INSERT INTO ...'||v_query
using p_id;
This will bind the value of p_id to the :p_id in the statement.
The very basic thing here is to capture the error appropriately so that you can able to track back. Here i have put loggers and EXCEPTION handlers jsut to check the error. Also i have put a RAISE_APPLICATION_ERROR. Did some modifications too in the snipet. Hope it helps.
PS: Have not checked for the syntax as i dont have workspace now with me.
CREATE OR REPLACE
PROCEDURE test_emp_sp(
p_id IN VARCHAR2)
AS
--Not needed
-- CURSOR rec
-- IS
-- SELECT dept,
-- source_value
-- FROM query_tb;
--Not needed
v_query VARCHAR2(1000);
BEGIN
FOR rec IN
(SELECT dept,source_value FROM query_tb
)
LOOP
IF rec.dept='CLERK' THEN
v_query :=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,mgr,hiredate,deptno,dname,loc) ('||v_query|| ')';
dbms_output.put_line(v_query||' inserted');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for Clerk--> '||SQLCODE||'---'||SQLERRM);
END;
ELSE
v_query:=REPLACE(rec.source_value,'#V_GCIF#',p_id);
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO emp_tb (empno,ename,job,deptno,dname,loc) ('||v_query||')';
dbms_output.put_line(v_query||' inserting others');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(' Error while inserting data in emp_tab for other then clerk --> '||SQLCODE||'---'||SQLERRM);
END;
END IF;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Error occurred in the plsql block',TRUE);
END;
/
I want Stored Procedure which create Temporary table using Create Table ... Select ... Statement.
And then select the records from same created table.
And finally drop that created table...
I want all these functionality in same stored procedure.
I have create the following stored procedure. But i got the below error
PL/SQL: ORA-00942: table or view does not exist
This one is my Procedure
DECLARE
TEMP_TBL VARCHAR2(4000);
TBL_NAME VARCHAR2(200) := 'ABC_TEST';
BEGIN
TEMP_TBL := 'CREATE TABLE MY_TAB_COL AS(SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,to_lob(DATA_DEFAULT) AS DATA_DEFAULT,NULLABLE FROM ALL_TAB_COLS WHERE TABLE_NAME=''' || TBL_NAME || ''')';
DBMS_OUTPUT.PUT_LINE(TEMP_TBL);
EXECUTE IMMEDIATE TEMP_TBL;
FOR DD_COLUMNS IN
(SELECT TABLE_NAME FROM MY_TAB_COL)
LOOP
DBMS_OUTPUT.PUT_LINE('DD_COLUMNS.TABLE_NAME');
END LOOP;
END;
I'm not sure why you want scenario like that, and not reccomend to use this code in production. I'm pretty sure in the 99% of the cases, this approach can be written in other way. But you can try the code bellow(Everything in your procedure should use dynamic sql)
declare
TBL_NAME varchar2(200) := 'ABC_TEST';
lv_Sql varchar2(4000);
begin
lv_Sql := 'CREATE TABLE MY_TAB_COL AS(SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,to_lob(DATA_DEFAULT) AS DATA_DEFAULT,NULLABLE FROM ALL_TAB_COLS WHERE TABLE_NAME=''' ||
TBL_NAME || ''')';
DBMS_OUTPUT.PUT_LINE(lv_Sql);
execute immediate lv_Sql;
lv_Sql := 'begin
for DD_COLUMNS in (select TABLE_NAME from MY_TAB_COL) loop
DBMS_OUTPUT.PUT_LINE(DD_COLUMNS.TABLE_NAME);
end loop;
end;';
DBMS_OUTPUT.PUT_LINE(lv_Sql);
execute immediate lv_Sql;
for R in (select *
from user_objects
where object_name = 'MY_TAB_COL'
and object_type = 'TABLE') loop
DBMS_OUTPUT.PUT_LINE('drop table ' || R.Object_Name);
execute immediate 'drop table ' || R.Object_Name;
end loop;
end;
Trying to check is table exist before create in Oracle. Search for most of the post from Stackoverflow and others too. Find some query but it didn't work for me.
IF((SELECT count(*) FROM dba_tables where table_name = 'EMPLOYEE') <= 0)
THEN
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)
END IF;
Which gives me error
Error: ORA-00900: invalid SQL statement
SQLState: 42000
ErrorCode: 900
Position: 1
I search for the syntax for IF condition, I think which is also write.
Please suggest me....
As Rene also commented, it's quite uncommon to check first and then create the table.
If you want to have a running code according to your method, this will be:
declare
nCount NUMBER;
v_sql LONG;
begin
SELECT count(*) into nCount FROM dba_tables where table_name = 'EMPLOYEE';
IF(nCount <= 0)
THEN
v_sql:='
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
END IF;
end;
But I'd rather go catch on the Exception, saves you some unnecessary lines of code:
declare
v_sql LONG;
begin
v_sql:='create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -955 THEN
NULL; -- suppresses ORA-00955 exception
ELSE
RAISE;
END IF;
END;
/
I know this topic is a bit old, but I think I did something that may be useful for someone, so I'm posting it.
I compiled suggestions from this thread's answers into a procedure:
CREATE OR REPLACE PROCEDURE create_table_if_doesnt_exist(
p_table_name VARCHAR2,
create_table_query VARCHAR2
) AUTHID CURRENT_USER IS
n NUMBER;
BEGIN
SELECT COUNT(*) INTO n FROM user_tables WHERE table_name = UPPER(p_table_name);
IF (n = 0) THEN
EXECUTE IMMEDIATE create_table_query;
END IF;
END;
You can then use it in a following way:
call create_table_if_doesnt_exist('my_table', 'CREATE TABLE my_table (
id NUMBER(19) NOT NULL PRIMARY KEY,
text VARCHAR2(4000),
modified_time TIMESTAMP
)'
);
I know that it's kinda redundant to pass table name twice, but I think that's the easiest here.
Hope somebody finds above useful :-).
Please try:
SET SERVEROUTPUT ON
DECLARE
v_emp int:=0;
BEGIN
SELECT count(*) into v_emp FROM dba_tables where table_name = 'EMPLOYEE';
if v_emp<=0 then
EXECUTE IMMEDIATE 'create table EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
END;
declare n number(10);
begin
select count(*) into n from tab where tname='TEST';
if (n = 0) then
execute immediate
'create table TEST ( ID NUMBER(3), NAME VARCHAR2 (30) NOT NULL)';
end if;
end;
My solution is just compilation of best ideas in thread, with a little improvement.
I use both dedicated procedure (#Tomasz Borowiec) to facilitate reuse, and exception handling (#Tobias Twardon) to reduce code and to get rid of redundant table name in procedure.
DECLARE
PROCEDURE create_table_if_doesnt_exist(
p_create_table_query VARCHAR2
) IS
BEGIN
EXECUTE IMMEDIATE p_create_table_query;
EXCEPTION
WHEN OTHERS THEN
-- suppresses "name is already being used" exception
IF SQLCODE = -955 THEN
NULL;
END IF;
END;
BEGIN
create_table_if_doesnt_exist('
CREATE TABLE "MY_TABLE" (
"ID" NUMBER(19) NOT NULL PRIMARY KEY,
"TEXT" VARCHAR2(4000),
"MOD_TIME" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
');
END;
/
Any solution which relies on testing before creation can run into a 'race' condition where another process creates the table between you testing that it does not exists and creating it. - Minor point I know.
-- checks for table in specfic schema:
declare n number(10);
begin
Select count(*) into n from SYS.All_All_Tables where owner = 'MYSCHEMA' and TABLE_NAME = 'EMPLOYEE';
if (n = 0) then
execute immediate
'create table MYSCHEMA.EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
end;
Well there are lot of answeres already provided and lot are making sense too.
Some mentioned it is just warning and some giving a temp way to disable warnings. All that will work but add risk when number of transactions in your DB is high.
I came across similar situation today and here is very simple query I came up with...
declare
begin
execute immediate '
create table "TBL" ("ID" number not null)';
exception when others then
if SQLCODE = -955 then null; else raise; end if;
end;
/
955 is failure code.
This is simple, if exception come while running query it will be suppressed. and you can use same for SQL or Oracle.
Its no need declare and count apply too.
begin
for rec in (select 1 from user_tables where table_name = 'YOUR_TABLE')
-- or
-- for rec in (select 1 from all_tables where table_name = 'YOUR_TABLE' and owner = 'YOU')
loop
execute immediate 'create table your_table as (f1 char(1))';
end loop;
end;
/
Will be good mode create check function
create or replace function this_object_exists (p_obj_name user_objects.object_name%type) return boolean
is
begin
for rec in (select 1 from user_objects where object_name = upper(p_obj_name))
loop
return true;
end loop;
return false;
end this_object_exists;
And thus use code for check exists
.
.
.
.
INDEX PARTITION
TABLE SUBPARTITION
SEQUENCE
TABLE PARTITION
PROCEDURE
LOB PARTITION
LOB
INDEX SUBPARTITION
PACKAGE
PACKAGE BODY
TYPE BODY
TRIGGER
INDEX
TABLE
VIEW
FUNCTION
SYNONYM
TYPE
JOB
...
begin
if not this_object_exists('your_table') then
execute immediate 'create table your_table as (f1 char(1))';
end if;
end;
or
begin
if this_object_exists('your_table') then
execute immediate 'drop table your_table';
end if;
execute immediate 'create table your_table as (f1 char(1))';
end;
I need a help on this.
I am trying to create a pl/sql anonymous block and when I run that it shows its completed but it don't run the code. It should give me an error saying name is already used by an existing object. Can someone help me on this.
I am actually creating procedures but just trying this code as a sample.
DECLARE
V_REF_TBL VARCHAR2(100);
V_SQL LONG;
begin
V_REF_TBL :='My_TABLE';
v_SQL :='truncate table '||V_REF_TBL ;
EXECUTE IMMEDIATE V_SQL;
EXECUTE IMMEDIATE 'CREATE TABLE '|| V_REF_TBL ||' parallel 9 nologging pctfree 0 as
select * from dual';
End;
Possibly you're looking for this:
<<my_block>>
Declare
table_name varchar2(30);
counter number;
Begin
table_name := 'my_table';
select count(*)
into counter
from user_tables
where table_name = Upper(Trim(my_block.table_name)) and
dropped = 'NO';
if counter = 0
then
execute immediate 'create table '||table_name||' as ... etc';
else
execute immediate 'truncate table '||table_name;
execute immediate 'insert into '||table_name' select ... etc';
end if;
end my_block;
I need to drop a table and make a new one. If I drop the table and the table doesn't exist, I get an error
How can I check if the table exists?
I'm working on Oracle 11g
Thanks in advance.
You could do something like this:
DECLARE v_exist PLS_INTEGER;
BEGIN
SELECT COUNT(*) INTO v_exist
FROM user_tables
WHERE table_name = 'YOURTABLEHERE';
IF v_exist = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE YOURTABLEHERE';
END IF;
DECLARE
eTABLE_OR_VIEW_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(eTABLE_OR_VIEW_DOES_NOT_EXIST, -942);
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE SCHEMA.WHATEVER';
EXCEPTION
WHEN eTABLE_OR_VIEW_DOES_NOT_EXIST THEN
NULL;
END;
Share and enjoy.
something like
select count(*) from user_tables
where table_name= :table name
or
select count(*) from dba_tables
where owner = :table owner
and table_name = :table name
or a heavy-handed alternative:
begin execute immediate 'drop table table_name';
exception when others then null;
end;
I have been using the following procedure to take care of this:
create or replace procedure drop_table_if_exists ( p_table_name varchar2 )
is
it_exist number;
begin
select count(*)
into it_exists
from user_tables
where table_name = p_table_name
;
if it_exists >= 1 then
execute immediate 'drop table '||p_table_name;
end if;
end;
/
exec drop_table_if_exists ( 'TABLE_TO_DROP' );