I am trying to use insert into statement, but getting the error:
ORA-01628: max # extents (32765) reached for rollback segment _SYSSMU134_1882489978$
Increasing the UNDO tablespace is not an option, so I would like a way to insert those data by chunks (for example with 1 million rows at a time). Can someone help to rewrite this procedure in that way?
CREATE OR REPLACE PROCEDURE create_chunks (
p_source_table IN VARCHAR2,
p_table_name_chunk IN VARCHAR2,
p_chunks IN VARCHAR2
) AS
v_insert_sql CLOB;
BEGIN
v_insert_sql := 'INSERT INTO ' || p_table_name_chunk ||
' (rid, chunk_number) ' ||
'SELECT /*+ parallel(64) */ rowid rid,' ||
'mod( ora_hash(rowid), :p_chunks ) as chunk_number '
'FROM ' || p_source_table;
EXECUTE IMMEDIATE v_insert_sql USING p_chunks;
COMMIT;
END;
This v_insert_sql is failing with above mentioned error. I have working solution using the cursor fetching like that:
DECLARE
CURSOR v_cur IS SELECT /*+ parallel(64) */
rowid rid, mod( ora_hash(rowid), 20000 ) AS chunk_number
-- I need this table to be parametric name
FROM some_table;
TYPE t_sample IS TABLE OF v_cur%ROWTYPE;
v_sample t_sample;
v_row_limit CONSTANT NUMBER := 1000000;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur BULK COLLECT INTO v_sample LIMIT v_row_limit;
FORALL i IN v_sample.first .. v_sample.last
INSERT INTO chunk_table VALUES v_sample(i);
COMMIT;
EXIT WHEN v_cur%NOTFOUND;
END LOOP;
CLOSE v_cur;
END;
I can't move this cursor straight into the procedure as the table name is varying and I need it to be parametric as with cursor approach I have to repeat the same code for different tables. So the question is how to deal with this?
Basically, I was able to just past the whole chunks approach inside procedure as dynamic query, like that:
CREATE OR REPLACE PROCEDURE create_chunks (
p_source_table IN VARCHAR2,
p_table_name_chunk IN VARCHAR2,
p_chunks IN VARCHAR2
) AS
v_insert_sql CLOB;
BEGIN
v_insert_sql := '' ||
' DECLARE ' || CHR(10) ||
' CURSOR cur1 IS SELECT ' || CHR(10) ||
' /*+ parallel(64) full(tbn)*/ rowid rid,' || CHR(10) ||
' mod( ora_hash(rowid), :p_chunks ) AS chunk_number' || CHR(10) ||
' FROM ' || p_source_table || ' tbn;' || CHR(10) ||
' TYPE t_sample IS TABLE OF cur1%ROWTYPE;' || CHR(10) ||
' v_sample t_sample;' || CHR(10) ||
' v_row_limit CONSTANT NUMBER := 1000000;' || CHR(10) ||
' BEGIN' || CHR(10) ||
' OPEN cur1;' || CHR(10) ||
' LOOP' || CHR(10) ||
' FETCH cur1 BULK COLLECT INTO v_sample LIMIT v_row_limit;' || CHR(10) ||
' FORALL i IN v_sample.first .. v_sample.last' || CHR(10) ||
' INSERT INTO ' || p_table_name_chunk || ' VALUES v_sample(i);' || CHR(10) ||
' COMMIT;' || CHR(10) ||
' EXIT WHEN cur1%NOTFOUND;' || CHR(10) ||
' END LOOP;' || CHR(10) ||
' CLOSE cur1;' || CHR(10) ||
' END;';
EXECUTE IMMEDIATE v_insert_sql USING p_chunks;
COMMIT;
END;
I'm trying to create a stored procedure where I'm passing select statement to for loop and i'm using dynamic table which is passing at runtime and getting below error:
LINE 23 PLS-00364: loop index variable 'I' use is invalid
LINE 19 PL/SQL: ORA-00942: table or view does not exist
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
BEGIN
FOR I in (SELECT DEL_IND FROM TBL_NM)
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
IF I.DEL_IND = 0
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || ' FROM ' || TBL_NM;
ELSIF I.DEL_IND = 1
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND,SRC_ID_NO) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
COMMIT;
END;
I call the procedure using:
EXEC CREATE_TEST('abc');
What you want is a REF CURSOR as you cannot use cursor for loop with dynamic sql.
I dont know the exact datatype for your column DEL_IND. Please declare accordingly.
Here is some more information on Oracle REF CURSORS.
Try below
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
TYPE c1ref is REF CURSOR;
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
DEL_IND NUMBER(5);
BEGIN
vsql_text := 'select DEL_IND from ' || TBL_NM;
open c1ref for vsql_text;
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
fetch c1ref into DEL_IND;
exit when c1ref%NOTFOUND;
IF (DEL_IND = 0)
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || FROM ' || TBL_NM;
ELSIF (DEL_IND = 1)
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
CLOSE c1ref;
COMMIT;
END;
I have below query which gives an error as encounter an symbol ( in the line where loop is used. I am trying to develop a function which takes dynamic paramater as table_name,column_name,table_id and used for other tables as well.
FUNCTION get_encryp_pass( table_name IN varchar2,column_name IN varchar2,table_id IN varchar2) RETURN VARCHAR2
IS
BEGIN
EXECUTE IMMEDIATE 'for c1 in (select * from' || table_name ||) loop
EXECUTE IMMEDIATE 'update ' || table_name || ' set ' || column_name = encrypt_val(c1.column_name) || ' where ' || table_id || ' = ' || c1.table_id and column_name is not null;
end loop;
END get_encrypt_pass;
this should work:
CREATE PROCEDURE get_encryp_pass(table_name IN varchar2,
column_name IN varchar2,
table_id IN varchar2) IS
BEGIN
EXECUTE IMMEDIATE 'begin for c1 in (select * from ' || table_name ||
') loop update ' || table_name || ' set ' ||
column_name || ' = encrypt_val(c1.' || column_name ||
') where ' || table_id || ' = c1.'||table_id||' and ' || column_name ||
' is not null; end loop; end;'
;
END;
But why not simply call update FTP_SFTP_SERVER set PASSWORD=encrypt_val(PASSWORD) where PASSWORD is not null ?
keep care of what is a variable and what is a string-literal and must be single-quoted therefore ... and string-variables mus be double-quoted:
EXECUTE IMMEDIATE 'update ' || table_name || ' set ' || column_name || ' = ''' || encrypt_val(c1.column_name) || ''' where ' || table_id || ' = ' || c1.table_id || ' and column_name is not null';
Best practice is to concatenate the statement in a varchar2-variable first and inspect this. If the content of the variable is syntactical correct and executable, the EXECUTE IMMEDIATE should work as well
declare
stmt varchar2(4000);
begin
stmt := 'update ' || table_name || ' set ' || column_name || ' = ''' || encrypt_val(c1.column_name) || ''' where ' || table_id || ' = ' || c1.table_id || ' and column_name is not null';
EXECUTE IMMEDIATE stmt;
end;
I think i have one alternative for your question. MERGE can be used in this case. Please try it i dont have workspaceso dint test it but it should work Let me know if it helps.
FUNCTION get_encryp_pass(
table_name IN VARCHAR2,
column_name IN VARCHAR2,
table_id IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
EXECUTE IMMEDIATE 'MERGE INTO '||table_name||' t1 USING
(
SELECT * FROM '||table_name|| ')t2
ON
(
t1.table_id = t2.table_id
)
WHEN MATCHED THEN
UPDATE SET t1.'||column_name||' = encrypt_val(t2.'||column_name||')'
||' WHERE and t1.'||column_name||' IS NOT NULL';
END;
Here is the code.
p_id number,
p_mc varchar2
Both of the above parameters will be passed in during a procedure call.
EXECUTE IMMEDIATE 'INSERT INTO COUNT_MASTER_TEMP ' || 'SELECT COUNT (ar.'|| p_mc || ')' ||
'
FROM app_recipient ar
WHERE EXISTS (SELECT r.' || p_mc ||
' FROM app_recipient r
WHERE r.ID =' || p_id || ' AND ar.'|| p_mc || '= r.'|| p_mc ||')';
My stored procedure is as follows:
input parameters are p_id (which is a numeric value), p_mc is a actually a column name. sample data is p_id = 6372325 and p_mc can be MC1, MC2 till MC14.
CREATE OR REPLACE PROCEDURE HCP_360.sp_get_all_records
(p_id NUMBER,
p_mc varchar2,
p_detail OUT SYS_REFCURSOR)
IS
v_count NUMBER;
v_master_id NUMBER;
v_sql VARCHAR2(2000);
--v_sql2 VARCHAR2(2000);
v_mc VARCHAR2(255):=p_mc;
cur_detail SYS_REFCURSOR;
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM COUNT_MASTER_TEMP';
execute immediate 'INSERT INTO COUNT_MASTER_TEMP ' ||
'SELECT COUNT (ar.'|| v_mc|| ')' || '
FROM app_recipient ar
WHERE EXISTS (SELECT r.' || v_mc || ' FROM app_recipient r
WHERE r.ID =' || p_id || ' AND ar.'|| v_mc || '= r.'|| v_mc||')';
END;
BEGIN
select c_count
into v_count
from COUNT_MASTER_TEMP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
v_count := 0;
END;
BEGIN
IF v_count > 0
THEN
v_sql := 'SELECT master_id
FROM app_recipient
WHERE ' || v_mc || ' IN (SELECT r.'||v_mc ||
' FROM app_recipient r
WHERE r.ID = ' || p_id || ')
AND ROWNUM <= 1
AND master_id IS NOT NULL
ORDER BY master_id DESC';
EXECUTE IMMEDIATE 'DELETE FROM COUNT_MASTER_TEMP';
EXECUTE IMMEDIATE 'INSERT INTO COUNT_MASTER_TEMP ' || v_sql;
END IF;
select c_count
into v_master_id
from COUNT_MASTER_TEMP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
v_master_id := 0;
END;
BEGIN
v_sql :=
'
SELECT r.ID,
r.master_id,
v.RECIPIENT_STATUS,
v.PARENT_OR_CHILD,
nvl(v.CHILD_COUNT, 0) CHILD_COUNT,
r.IS_PICKABLE,
r.IS_GOLDEN,
r.request_wf_state,
r.record_type,
r.first_name,
r.last_name,
r.'||v_mc ||
',r.middle,
r.title,
r.name_of_organization,
r.name_of_business,
r.address,
r.city,
r.state,
r.country,
v.HCP_TYPE,
v.HCP_SUBTYPE,
v.is_edit_locked,
v.record_type as rec_type,
v.DATA_SOURCE_NAME,
v.DEA_DATA,
v.NPI_DATA,
v.STATE_DATA,
RPPS,
v.finess,
v.siren_number
FROM app_recipient r
left join V_MASTER_RECIP_W_TRXN_OP v
on r.id = v.id
or r.master_id =' || v_master_id||
'WHERE' ||'r.'||v_mc || '= ANY
(SELECT ar.'||v_mc || 'FROM app_recipient ar WHERE r.ID =' || p_id || ')
UNION ALL
SELECT r.ID,
r.master_id,
v.RECIPIENT_STATUS,
v.PARENT_OR_CHILD,
nvl(v.CHILD_COUNT, 0) CHILD_COUNT,
r.IS_PICKABLE,
r.IS_GOLDEN,
r.request_wf_state,
r.record_type,
r.first_name,
r.last_name,
r.'||v_mc ||
',r.middle,
r.title,
r.name_of_organization,
r.name_of_business,
r.address,
r.city,
r.state,
r.country,
v.HCP_TYPE,
v.HCP_SUBTYPE,
v.is_edit_locked,
v.record_type as rec_type,
v.DATA_SOURCE_NAME,
v.DEA_DATA,
v.NPI_DATA,
v.STATE_DATA,
RPPS,
v.finess,
v.siren_number
FROM app_recipient r
left join V_MASTER_RECIP_W_TRXN_OP2 v
on r.id = v.id
or r.master_id =' || v_master_id ||'
WHERE r.'||v_mc ||'= ANY (SELECT ar.'||v_mc ||'FROM app_recipient ar WHERE r.ID =' ||v_master_id || ')';
open cur_detail for v_sql;
p_detail := cur_detail;
end;
END;
/
if you use pid as a VARCHAR with non-numeric data.. and you have embed it in single quotes, during query formation, or define it as a bind variable.
Always use bind variables where-ever possible.
EXECUTE IMMEDIATE
'INSERT INTO COUNT_MASTER_TEMP ' ||
'SELECT COUNT (ar.'|| p_mc || ')' ||
' FROM app_recipient ar ' ||
' WHERE EXISTS (SELECT r.' || p_mc ||
' FROM app_recipient r ' ||
' WHERE r.ID = :pid AND ar.'|| p_mc || '= r.'|| p_mc||')'
using pid;
You could test the string formation using DBMS_OUTPUT.
For example,
SQL> set serveroutput on
SQL> DECLARE
2 p_mc VARCHAR2(20);
3 p_id NUMBER;
4 BEGIN
5 p_mc := 'mc';
6 p_id := 1;
7 dbms_output.put_line( 'INSERT INTO COUNT_MASTER_TEMP ' ||
8 'SELECT COUNT (ar.'|| p_mc || ')' || '
9 FROM app_recipient ar
10 WHERE EXISTS (SELECT r.' || p_mc || ' FROM app_recipient r
11 WHERE r.ID =' || p_id || ' AND ar.'|| p_mc || '= r.'|| p_mc||')');
12 END;
13 /
INSERT INTO COUNT_MASTER_TEMP SELECT COUNT (ar.mc)
FROM app_recipient ar
WHERE
EXISTS (SELECT r.mc FROM app_recipient r
WHERE r.ID =1 AND ar.mc= r.mc)
PL/SQL procedure successfully completed.
SQL>
Always remember to first test the dynamic string using DBMS_OUTPUT. Once you are confirm that the string is correctly formed, remove/comment the dbms_output and use EXECUTE IMMEDIATE.
Update Thanks to Alex Poole. The actual issue is here:
'WHERE' ||'r.'||v_mc || '= ANY
After WHERE, there is no space, thus the query fails. You need to add a space after WHERE.
'WHERE ' ||'r.'||v_mc || '= ANY
The only way I can immediately see to get that error from insert is if you're passing the column name enclosed in single-quotes; you haven't shown the call and it's unlikely you'd do this from a SQL client (more likely you added quotes incorrectly in a JDBC/PHP/etc. parameter), but calling like this:
exec sp_get_all_records(6372325, '''MC1''', :detail);
would try to execute generated SQL that looks like this:
INSERT INTO COUNT_MASTER_TEMP SELECT COUNT (ar.'MC1')
FROM app_recipient ar
WHERE EXISTS (SELECT r.'MC1' FROM app_recipient r
WHERE r.ID =6372325 AND ar.'MC1'= r.'MC1')
with the column name starting with the illegal single-quote character, and that would indeed throw:
ORA-01747: invalid user.table.column, table.column, or column specification
ORA-06512: at "SCHEMA.SP_GET_ALL_RECORDS", line 17
ORA-06512: at line 1
and line 17 is the execute immediate 'INSERT....
You are also missing some whitespace in your final v_sql construct, which will case the open cur_detail to get an "ORA-00933: SQL command not properly ended" with what you've shown. You need to change
'WHERE' ||'r.'||v_mc || '= ANY
(SELECT ar.'||v_mc || 'FROM app_recipient ar WHERE r.ID =' || p_id || ')
to
' WHERE ' ||'r.'||v_mc || '= ANY
(SELECT ar.'||v_mc || ' FROM app_recipient ar WHERE r.ID =' || p_id || ')
with spaces ether side of the first WHERE, and before the FROM; and also in the second half of the union change
WHERE r.'||v_mc ||'= ANY (SELECT ar.'||v_mc ||'FROM app_recipient ar WHERE r.ID =' ||v_master_id || ')';
to
WHERE r.'||v_mc ||'= ANY (SELECT ar.'||v_mc ||' FROM app_recipient ar WHERE r.ID =' ||v_master_id || ')';
again adding a space before FROM.
Those won't get the ORA-01747 error you reported, but neither will the insert you showed, which seems to be valid with the arguments you are passing - unless you are incorrectly quoting the column name argument as shown above.
I was testing my procedure using PL/SQL developer. I still do not know why ORA-01747 was occurring. But I modified my procedure and wrote a PL/SQL code for testing the output.
Procedure is as follows:
CREATE OR REPLACE PROCEDURE HCP_360.sp_get_all_records
(p_id NUMBER,
p_mc in varchar2,
p_detail OUT SYS_REFCURSOR,
p_count OUT NUMBER,
p_error OUT NVARCHAR2)
IS
v_count NUMBER;
v_master_id NUMBER;
v_sql LONG;
--v_sql2 VARCHAR2(2000);
v_mc VARCHAR2(255):=p_mc;
cur_detail SYS_REFCURSOR;
BEGIN
BEGIN
-- v_sql := '(SELECT COUNT ( ' ||v_mc || ')' ||
-- '
-- FROM app_recipient
-- WHERE ' ||v_mc || ' IN (SELECT ' || v_mc ||
-- ' FROM app_recipient r
-- WHERE r.ID =' || p_id || ' AND '|| v_mc || '=' || v_mc ||'))';
v_sql :='(SELECT ar.'||v_mc || ' as v_count' ||
'
FROM app_recipient ar
WHERE EXISTS (SELECT r.'||v_mc ||
' FROM app_recipient r
WHERE r.ID =' || p_id || ' AND ar.'||v_mc || '= r.'||v_mc ||'))';
--EXECUTE IMMEDIATE 'DELETE FROM COUNT_MASTER_TEMP';
EXECUTE IMMEDIATE ' select nvl(count(*),0) as count from (' || v_SQL || ') '
INTO v_count;
END;
BEGIN
IF v_count > 0
THEN
v_sql := 'SELECT master_id
FROM app_recipient
WHERE ' || v_mc || ' IN (SELECT r.'||v_mc ||
' FROM app_recipient r
WHERE r.ID = ' || p_id || ')
AND ROWNUM <= 1
AND master_id IS NOT NULL
ORDER BY master_id DESC';
EXECUTE IMMEDIATE ' select nvl(master_id, 0) from (' || v_SQL || ') '
INTO v_master_id;
END IF;
END;
BEGIN
EXECUTE IMMEDIATE ' delete from GTT_RECIPEINTS_FOR_MC';
execute immediate 'insert into GTT_RECIPEINTS_FOR_MC
( MATCH_CODE_VALUE ,master_id ,id)
(SELECT r.'||v_mc ||', r.master_id, r.id
FROM app_recipient r
WHERE r.'||v_mc || '= ANY (SELECT r.'||v_mc || '
FROM app_recipient r
WHERE r.ID =' || p_id||')
UNION ALL
SELECT r.'||v_mc ||', r.master_id, r.id
FROM app_recipient r
WHERE r.ID =' ||v_master_id ||')';
end;
BEGIN
v_sql :=
'
select ( r.id) M_ID, r.RECIPIENT_STATUS, PARENT_OR_CHILD, nvl(CHILD_COUNT,0), r.IS_PICKABLE, r.IS_GOLDEN, r.ID, nvl(r.MASTER_ID,r.id) as MASTER_ID , r.request_wf_state,
r.TITLE, r.FIRST_NAME, r.MIDDLE, r.LAST_NAME,r.FULL_NAME_LNF, r.FULL_NAME_FNF, r.NAME_OF_ORGANIZATION,r.ADDRESS,
r.CITY, r.STATE, r.COUNTRY, r.HCP_TYPE, r.HCP_SUBTYPE, r.is_edit_locked, r.record_type as rec_type,
DATA_SOURCE_NAME,DEA_DATA,NPI_DATA,STATE_DATA,RPPS ,finess,siren_number from v_master_recip_w_trxn_op r join GTT_RECIPEINTS_FOR_MC p on r.id=p.id or r.id= p.master_id
order by id';
open cur_detail for v_sql;
p_detail := cur_detail;
p_count :=SQL%ROWCOUNT;
EXCEPTION
WHEN OTHERS
THEN
p_count := 0;
ROLLBACK;
end;
END;
/
And the PL/SQL code for testing is as follows:
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor SYS_REFCURSOR;
p_count number;
p_error NVARCHAR2 (255);
M_ID app_recipient.id%type ;
l_ID app_recipient.id%type ;
l_master_id app_recipient.master_id%type ;
l_RECIPIENT_STATUS V_MASTER_RECIP_W_TRXN_OP2.recipient_status%type;
l_PARENT_OR_CHILD V_MASTER_RECIP_W_TRXN_OP2.parent_or_child%type;
l_CHILD_COUNT V_MASTER_RECIP_W_TRXN_OP2.child_count%type;
l_IS_PICKABLE app_recipient.is_pickable%type;
l_IS_GOLDEN app_recipient.is_golden%type;
l_request_wf_state app_recipient.request_wf_state%type;
l_record_type app_recipient.record_type%type;
l_first_name app_recipient.first_name%type;
l_last_name app_recipient.last_name%type;
l_p_mc app_recipient.MC14%type;
l_middle app_recipient.middle%type;
l_title app_recipient.title%type;
l_name_of_organization app_recipient.name_of_organization%type;
l_name_of_business app_recipient.name_of_business%type;
l_address app_recipient.address%type;
l_city app_recipient.city%type;
l_state app_recipient.state%type;
l_country app_recipient.country%type;
l_HCP_TYPE v_master_recip_w_trxn_op.HCP_TYPE%type;
l_HCP_SUBTYPE V_MASTER_RECIP_W_TRXN_OP.HCP_SUBTYPE%type;
l_is_edit_locked V_MASTER_RECIP_W_TRXN_OP.is_edit_locked%type;
l_rec_type V_MASTER_RECIP_W_TRXN_OP.record_type%type;
l_DATA_SOURCE_NAME V_MASTER_RECIP_W_TRXN_OP.data_source_name%type;
l_DEA_DATA V_MASTER_RECIP_W_TRXN_OP.dea_data%type;
l_NPI_DATA V_MASTER_RECIP_W_TRXN_OP.npi_data%type;
l_STATE_DATA V_MASTER_RECIP_W_TRXN_OP.state_data%type;
l_RPPS V_MASTER_RECIP_W_TRXN_OP.rpps%type;
l_finess V_MASTER_RECIP_W_TRXN_OP.finess%type;
l_siren_number V_MASTER_RECIP_W_TRXN_OP.siren_number%type;
l_FULL_NAME_LNF V_MASTER_RECIP_W_TRXN_OP.siren_number%type;
l_FULL_NAME_FNF V_MASTER_RECIP_W_TRXN_OP.siren_number%type;
BEGIN
hcp_360.hcp360_application.get_recipients_for_mc(p_id => &&var,
p_mc => &&p_mc,
p_detail => l_cursor,
p_count => p_count,
p_error => p_error);
LOOP
FETCH l_cursor
INTO M_ID ,
l_RECIPIENT_STATUS ,
l_PARENT_OR_CHILD ,
l_CHILD_COUNT ,
l_IS_PICKABLE,
l_IS_GOLDEN,
l_ID,
L_MASTER_ID,
l_request_wf_state,
l_title,
l_first_name,
l_middle,
l_last_name,
l_FULL_NAME_LNF,
l_FULL_NAME_FNF,
l_name_of_organization,
l_address,
l_city,
l_state,
l_country,
l_HCP_TYPE,
l_HCP_SUBTYPE,
l_is_edit_locked,
l_rec_type,
l_DATA_SOURCE_NAME,
l_DEA_DATA,
l_NPI_DATA,
l_STATE_DATA,
l_RPPS,
l_finess,
l_siren_number;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(M_ID || ' | ' ||
l_RECIPIENT_STATUS || ' | ' ||
l_PARENT_OR_CHILD || ' | ' ||
l_CHILD_COUNT || ' | ' ||
l_IS_PICKABLE || ' | ' ||
l_IS_GOLDEN || ' | ' ||
l_ID || ' | ' ||
L_MASTER_ID || ' | ' ||
l_request_wf_state || ' | ' ||
l_title || ' | ' ||
l_first_name || ' | ' ||
l_middle || ' | ' ||
l_last_name || ' | ' ||
l_FULL_NAME_LNF || ' | ' ||
l_FULL_NAME_FNF || ' | ' ||
l_name_of_organization || ' | ' ||
l_address || ' | ' ||
l_city || ' | ' ||
l_state || ' | ' ||
l_country || ' | ' ||
l_HCP_TYPE || ' | ' ||
l_HCP_SUBTYPE || ' | ' ||
l_is_edit_locked || ' | ' ||
l_rec_type || ' | ' ||
l_DATA_SOURCE_NAME || ' | ' ||
l_DEA_DATA || ' | ' ||
l_NPI_DATA || ' | ' ||
l_STATE_DATA || ' | ' ||
l_RPPS || ' | ' ||
l_finess || ' | ' ||
l_siren_number
);
END LOOP;
CLOSE l_cursor;
END;
I have a stored procedure in Oracle 11g that will delete records for specific clients in some tables. The below script is an example of its set up. It will continue to run each block even if an exception is raised. We want it to stop running if any other exception occurs except "-942 table does not exist." If the table does not exist, the rest of the procedure should continue to run, all others should cause it to stop. How can I do this?
create or replace PROCEDURE SCHEMA.PURGE_RECORDS
(vCLIENT_ID IN VARCHAR2, pINPUTSCOPE IN VARCHAR2,
pSUCCESS_IND OUT VARCHAR2, pOUTCOME_DESC OUT VARCHAR2)
AS
VTABLE_NAME VARCHAR2(200);
vSQL VARCHAR2(10000);
BEGIN
IF pINPUTSCOPE = 'ALL' THEN
BEGIN
VTABLE_NAME := 'TABLE NAME';
vSQL := '
DELETE FROM TABLENAME T WHERE EXISTS (SELECT 1 FROM TABLE2 TSK WHERE CLIENT = '''|| vCLIENT_ID ||''' AND ASK_ID = T.ASK_ID) ';
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION WHEN OTHERS THEN NULL; DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
END;
BEGIN
VTABLE_NAME := 'TABLE NAME';
vSQL := '
DELETE FROM TABLENAME3 T WHERE EXISTS (SELECT 1 FROM TABLE3 TSK WHERE CLIENT = '''|| vCLIENT_ID ||''' AND ASK_ID = T.ASK_ID) ';
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION WHEN OTHERS THEN NULL; DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
END;
BEGIN
VTABLE_NAME := 'TABLE NAME';
vSQL := '
DELETE FROM TABLENAME4 T WHERE EXISTS (SELECT 1 FROM TABLE4 TSK WHERE CLIENT = '''|| vCLIENT_ID ||''' AND ASK_ID = T.ASK_ID) ';
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION WHEN OTHERS THEN NULL; DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
END;
BEGIN
VTABLE_NAME := 'TABLE NAME';
vSQL := '
DELETE FROM TABLENAME5 T WHERE EXISTS (SELECT 1 FROM TABLE5 TSK WHERE CLIENT = '''|| vCLIENT_ID ||''' AND ASK_ID = T.ASK_ID) ';
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION WHEN OTHERS THEN NULL; DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
END;
END IF;
END;
You can explicitly catch the ORA-00942 error, report it you want to, and then ignore it; and then either handle all other exceptions with a RAISE - which will propagate the exception to the next block, in this case causing the procedure to terminate - or don't catch them at all.
ORA-00942 isn't one of the predefined exceptions so you need to define an exception name and use the PRAGMA EXCEPTION_INIT clause to associate the exception to the internally defined exception number:
...
AS
VTABLE_NAME VARCHAR2(200);
vSQL VARCHAR2(10000);
NO_SUCH_TABLE EXCEPTION;
PRAGMA EXCEPTION_INIT (NO_SUCH_TABLE, -942);
BEGIN
IF pINPUTSCOPE = 'ALL' THEN
BEGIN
VTABLE_NAME := 'TABLE NAME';
vSQL := '
DELETE FROM TABLENAME T WHERE EXISTS (SELECT 1 FROM TABLE2 TSK WHERE CLIENT = '''|| vCLIENT_ID ||''' AND ASK_ID = T.ASK_ID) ';
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION
WHEN NO_SUCH_TABLE THEN
DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
-- this exception has been squashed
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
RAISE;
END;
...
And repeat the exception catch in each sub-block. Any other exception will still be caught by your OTHERS handler. Catching and squashing OTHERS is generally a bad idea, and it would be better to remove that and let the exceptions propagate naturally; even though you're displaying the error (assuming whoever runs this is showing the output), you're losing the line number of the original problem. So you only really need to do:
...
EXECUTE IMMEDIATE vSQL;
DBMS_OUTPUT.PUT_LINE ( VTABLE_NAME || ' Scope set: ' || pINPUTSCOPE || ' ' || '(' || TO_CHAR(SQL%ROWCOUNT) || ' ROWS DELETED)' || chr(10));
EXCEPTION
WHEN NO_SUCH_TABLE THEN
DBMS_OUTPUT.PUT_LINE('ERROR : ' || VTABLE_NAME || ' ' || SQLERRM);
-- this exception has been squashed, all others will propagate
END;
...