Procedure not processing 'table exists' exception handler in plsql - oracle

I've created a procedure to create backup tables which is failing to pick up the IF statement under the 'table_exists' exception handler. It is meant to drop and recreate the backup table if it already exists AND the do_not_delete column in the user.record table is set to 'NO':
{CREATE OR REPLACE PROCEDURE USER.CREATE_BACKUP_TABLE (t_owner varchar2, t_name varchar2, t_retention number, t_can_delete_tag varchar2) as
/*
*******************************************************************************************************
Object Name: CREATE_BACKUP_TABLE
*/-- Private variables
icnt number;
sqlcmd varchar2(1000);
backup_table varchar2(1000);
l_affx varchar2(30) := 'BKP';
l_date varchar2(30) := to_char(sysdate,'yyyy_mm_dd');
table_exists exception;
table_does_not_exist exception;
pragma exception_init(table_exists, -00955);
pragma exception_init(table_does_not_exist, -00942);
l_delete USER.RECORD.DO_NOT_DELETE%type;
--
--
begin
for tn in (
select table_name, owner
from all_tables
where table_name = t_name
and owner = t_owner
)
LOOP
BEGIN
backup_table := tn.owner||'.'||tn.table_name||'_'||l_date||'_'||l_affx;
sqlcmd := 'create table '||backup_table||' AS SELECT * FROM '||tn.owner||'.'||tn.table_name;
dbms_output.put_line(sqlcmd);
execute immediate sqlcmd;
--
exception
when table_exists then
sqlcmd := 'select do_not_delete into :l_delete from user.record where backup_table = '''||backup_table||'''';
execute immediate sqlcmd;
if l_delete = 'NO' then
sqlcmd := 'drop table '||backup_table||' cascade constraints';
dbms_output.put_line(sqlcmd);
execute immediate sqlcmd;
sqlcmd := 'create table '||backup_table||' AS SELECT * FROM '||tn.owner||'.'||tn.table_name;
execute immediate sqlcmd;
else if l_delete = 'YES' then
dbms_output.put_line('DML Action: Rollback');
Rollback;
end if;
end if;
when others then
dbms_output.put_line('ERROR: Preceeding statement failed with '||substr(sqlerrm(sqlcode),1,120));
dbms_output.put_line('DML Action: Rollback');
Rollback;
end;
END LOOP;
end;
/}

It's not the correct way to get the result from an execute immediate:
use "execute immediate ... returning into..." instead.
And by the way, in your case you don't need to use execute immediate to get the do_not_delete value, you can "select ... into " directly since the name of the table in the FROM clause is not a variable.

Related

Oracle Function Select * from Query Not giving any Results

The following is the function definition I am using:
CREATE OR REPLACE FUNCTION MA_FACTSET.totalCustomers
RETURN number as
total INTEGER := 0;
FID VARCHAR2(30) := 'NT33H0-S-US';
stmt varchar2(1000);
BEGIN
execute immediate 'TRUNCATE TABLE MovingAverage';
execute immediate 'DROP TABLE MovingAverage';
execute immediate 'CREATE GLOBAL TEMPORARY TABLE MovingAverage
(ID INTEGER,
PDATE DATE,
PPRICE FLOAT,
MA1 FLOAT) ON COMMIT PRESERVE ROWS';
stmt := 'INSERT INTO MovingAverage (ID,PDATE,PPRICE) SELECT
ROWNUM,"DATE",P_PRICE FROM FP_BASIC_BD WHERE FS_PERM_SEC_ID = ''NT33H0-S-US''';
DBMS_OUTPUT.PUT_LINE(stmt);
execute immediate stmt;
dbms_output.put_line(SQL%ROWCOUNT);
commit;
execute immediate 'UPDATE MovingAverage A SET MA1 =(SELECT AVG(PPRICE)
FROM MovingAverage WHERE ID>=A.ID-5 AND ID<A.ID )' ;
dbms_output.put_line(SQL%ROWCOUNT);
commit;
execute immediate 'SELECT * FROM MovingAverage ORDER BY ID ASC';
dbms_output.put_line(SQL%ROWCOUNT);
commit;
dbms_output.put_line(total);
RETURN total;
END;
/
I have applied some checks to see whether the function is running properly or not, like getting SQL%ROWCOUNT.
The following is the DBMS output of the function:
INSERT INTO MovingAverage (ID,PDATE,PPRICE) SELECT ROWNUM,"DATE",P_PRICE FROM FP_BASIC_BD WHERE FS_PERM_SEC_ID = 'NT33H0-S-US'
4114
4114
0
0
I want know why rowcount of "SELECT * FROM MovingAverage ORDER BY ID ASC" is 0. And if there is any way to print complete table in Data Grid. I am using TOAD for Oracle.
I suggest you to rewrite your function removing dynamic sqls where not necessary. Also, If you have DDLs, DMLs don't use PL/SQL function, rather use procedure.
For select Statements in EXECUTE IMMEDIATE, it will work if you use BULK COLLECT INTO
CREATE OR REPLACE FUNCTION totalCustomers
RETURN NUMBER
AS
total INTEGER := 0;
FID VARCHAR2 (30) := 'NT33H0-S-US';
stmt VARCHAR2 (1000);
TYPE mrectype IS TABLE OF MovingAverage%ROWTYPE;
mrec mrectype;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE MovingAverage';
EXECUTE IMMEDIATE 'DROP TABLE MovingAverage';
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE MovingAverage
(ID INTEGER,
PDATE DATE,
PPRICE FLOAT,
MA1 FLOAT) ON COMMIT PRESERVE ROWS';
stmt :=
'INSERT INTO MovingAverage (ID,PDATE,PPRICE) SELECT
ROWNUM,"DATE",P_PRICE FROM FP_BASIC_BD WHERE FS_PERM_SEC_ID = ''NT33H0-S-US''';
DBMS_OUTPUT.PUT_LINE (stmt);
EXECUTE IMMEDIATE stmt;
DBMS_OUTPUT.put_line (SQL%ROWCOUNT);
COMMIT;
EXECUTE IMMEDIATE 'UPDATE MovingAverage A SET MA1 =(SELECT AVG(PPRICE)
FROM MovingAverage WHERE ID>=A.ID-5 AND ID<A.ID )';
DBMS_OUTPUT.put_line (SQL%ROWCOUNT);
COMMIT;
EXECUTE IMMEDIATE 'SELECT * FROM MovingAverage ORDER BY ID ASC'
BULK COLLECT INTO mrec;
DBMS_OUTPUT.put_line (SQL%ROWCOUNT);
COMMIT;
DBMS_OUTPUT.put_line (total);
RETURN total;
END;
/
Call this function like this.
SET SERVEROUTPUT ON
DECLARE
x NUMBER;
BEGIN
x := totalCustomers;
END;
/
You cannot call the function in SQL select
With collections and the table function, a function can return a table that can be queried in an SQL statement. This is demonstrated in the following example.
Create your record type variable first.
create or replace type t_record as object (i number,n varchar2(30));
Create table type variable which is based on record type variable.
create or replace type t_table as table of t_record;
Then we can proceed to create a function as follows.
create or replace function return_table return t_table as
v_ret t_table;
begin
--
-- Call constructor to create the returned
-- variable:
--
v_ret := t_table();
--
-- Add one record after another to the returned table.
-- Note: the »table« must be extended before adding
-- another record:
--
v_ret.extend; v_ret(v_ret.count) := t_record(1, 'one' );
v_ret.extend; v_ret(v_ret.count) := t_record(2, 'two' );
v_ret.extend; v_ret(v_ret.count) := t_record(3, 'three');
--
-- Return the record:
--
return v_ret;
end return_table;
For more details and further extension of work please refer following URL.
https://renenyffenegger.ch/notes/development/databases/Oracle/PL-SQL/collection-types/return-table-from-function/index
http://www.adp-gmbh.ch/ora/plsql/coll/return_table.html

as it should be this code? Its a Oracle Procedure with if and else

Please I try to make a procedure to copy from one table to another and then delete the contents of the previous with if and else sentences. (PERSONA1 and PERSONA1_BKP are created)
create or replace procedure TRASLADO(DNI VARCHAR2)
as
v_DNI varchar2(8);
v_GP char(6);
v_PRIMER varchar2(40);
v_SEGUNDO varchar2(40);
v_PRENOM varchar2(40);
v_GENERO char(1);
v_FECHA DATE;
begin
SELECT NU_DNI, GP_VOTACION, AP_PRIMER, AP_SEGUNDO, PRENOM_INSCRITO, DE_GENERO, FE_INSCRIPCION
INTO v_DNI, v_GP, v_PRIMER, v_SEGUNDO, v_PRENOM, v_GENERO, v_FECHA /*se usa into para que la variable pueda usar lo que se le envia*/
FROM PERSONA1
WHERE NU_DNI=DNI;
IF (NU_DNI=DNI)
BEGIN
insert into PERSONA1_BKP values(v_DNI, v_GP, v_PRIMER, v_SEGUNDO, v_PRENOM, v_GENERO, v_FECHA, SYSDATE);
delete from PERSONA1 where DNI LIKE NU_DNI;
END
ELSE
BEGIN
dbms_output.put_line('EL DNI NO EXISTE');
END
end TRASLADO;
begin
execute immediate 'drop table table PERSONA1_BKP';
execute immediate 'create table PERSONA1_BKP as select * from PERSONA1';
execute immediate 'truncte table PERSONA1';
end;
V2: (using if / else statements)
begin
if false then
null;
else
execute immediate 'drop table table PERSONA1_BKP';
execute immediate 'create table PERSONA1_BKP as select c1 cn1, c2 cn2 from PERSONA1';
execute immediate 'truncte table PERSONA1';
end if;
end;

Dynamic SQL and Bulk Insert

I want to use the execute immediate command, to insert collection below PROCEDURE
CREATE OR REPLACE PROCEDURE P_LOAD_HLD(
p_app_name IN VARCHAR2,
p_filename IN VARCHAR2,
p_rectype IN VARCHAR2,
p_tabname IN VARCHAR2,
o_status OUT Number)
IS
type t_tab is table of holding_template%rowtype;
v_tab t_tab;
process_size number := 10000;
dummy_cursor SYS_REFCURSOR;
BEGIN
o_status := 0;
null;
if p_rectype='my_test' THEN
OPEN dummy_cursor FOR
select *
from TAB_staging a
where a.record_type = p_filename;
end if;
LOOP
BEGIN
FETCH dummy_cursor BULK COLLECT INTO V_TAB LIMIT PROCESS_SIZE;
FORALL I IN V_TAB.FIRST .. V_TAB.LAST
execute immediate 'insert into '||p_tabname||' values v_tab(i))';
EXCEPTION WHEN OTHERS THEN
FOR I IN 1 .. V_TAB.COUNT
LOOP
BEGIN
EXECUTE IMMEDIATE ' INSERT INTO '||p_tabname || ' VALUES ( '||
'v_tab(i).k0,v_tab(i).k1,v_tab(i).k2,v_tab(i).k3,v_tab(i).k4';
COMMIT;
EXCEPTION WHEN OTHERS THEN
RAISE;
END;
END LOOP;
END;
END LOOP;
END P_LOAD_HLD ;
this gives me the error message.
[Error] PLS-00435 (742: 7): PLS-00435: DML statement without BULK
In-BIND cannot be used inside FORALL

How to assign a table name to a variable and using the same inside immediate execute statement

I am trying to assign table name to a variable and to use the same inside execute immediate statement , please help me with the syntax inside execute statement
scenario is
I am trying to pass a table name as parameter in the procedure and am using them inside execute immediate to write into some other table .
please suggest me the syntax for in parameters and for the execute immediate statement.
My Procedure:
CREATE OR REPLACE PROCEDURE tep
AS
v_sql VARCHAR2(1024) ;
a NUMBER;
b VARCHAR2(30) :='t_stg1_non_silicon_purchace_re';
BEGIN
v_sql := q'[SELECT COUNT(*) FROM b ]';
EXECUTE immediate v_sql INTO a;
DBMS_OUTPUT.PUT_LINE( TO_CHAR(v_sql));
DBMS_OUTPUT.PUT_LINE( TO_CHAR(a));
END;
/
You haven't specified what exactly you want to do with the table name in the execute immediate so see example below of how to "CREATE" a table.
DECLARE
vTableName VARCHAR2(40 CHAR);
vSQL VARCHAR2(1000 CHAR);
BEGIN
vTableName:='MY_TABLE_NAME';
vSQL:='CREATE TABLE ' || vTableName || ' (COL1 NUMBER, COL2 NUMBER)';
EXECUTE IMMEDIATE vSQL;
END;
/
This query may satisfy your requirement. Thanks
CREATE OR REPLACE PROCEDURE tep
AS
v_sql VARCHAR2(1024) ;
a NUMBER;
b VARCHAR2(30) :='t_stg1_non_silicon_purchace_re';
BEGIN
v_sql := 'SELECT COUNT(*) FROM'||' '||b;
EXECUTE immediate v_sql INTO a;
DBMS_OUTPUT.PUT_LINE( TO_CHAR(v_sql));
DBMS_OUTPUT.PUT_LINE( TO_CHAR(a));
END;

Oracle, drop table if it exists AND empty

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;

Resources