I use the following Code to execute an export data pump.
set serveroutput on;
DECLARE
ind NUMBER; -- Loop index
h1 NUMBER; -- Data Pump job handle
percent_done NUMBER; -- Percentage of job complete
job_state VARCHAR2(30); -- To keep track of job state
le ku$_LogEntry; -- For WIP and error messages
js ku$_JobStatus; -- The job status from get_status
jd ku$_JobDesc; -- The job description from get_status
sts ku$_Status; -- The status object returned by get_status
>>>>>>>>>>>>>>v_systimestamp TIMESTAMP := SYSTIMESTAMP;<<<<<<<<<<<<<<
BEGIN
h1 := DBMS_DATAPUMP.OPEN('EXPORT','SCHEMA',NULL,'EXAMPLE3','LATEST');
DBMS_DATAPUMP.ADD_FILE(h1, 'dumpfile.dmp', 'EXPORT_DIRECTORY', NULL, DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE, 1);
DBMS_DATAPUMP.METADATA_FILTER(h1,'SCHEMA_EXPR','IN (''SchemaName'')');
DBMS_DATAPUMP.START_JOB(h1);
percent_done := 0;
job_state := 'UNDEFINED';
while (job_state != 'COMPLETED') and (job_state != 'STOPPED') loop
DBMS_OUTPUT.PUT_LINE(v_systimestamp);
dbms_datapump.get_status(h1,
dbms_datapump.ku$_status_job_error +
dbms_datapump.ku$_status_job_status +
dbms_datapump.ku$_status_wip,-1,job_state,sts);
js := sts.job_status;
-- If the percentage done changed, display the new value.
if js.percent_done != percent_done
then
dbms_output.put_line('*** Job percent done = ' ||
to_char(js.percent_done));
percent_done := js.percent_done;
end if;
-- If any work-in-progress (WIP) or error messages were received for the job,
-- display them.
if (bitand(sts.mask,dbms_datapump.ku$_status_wip) != 0)
then
le := sts.wip;
else
if (bitand(sts.mask,dbms_datapump.ku$_status_job_error) != 0)
then
le := sts.error;
else
le := null;
end if;
end if;
if le is not null
then
ind := le.FIRST;
while ind is not null loop
>>>>>>>>>>>>>>DBMS_OUTPUT.PUT_LINE(v_systimestamp);<<<<<<<<<<<<<<
dbms_output.put_line(le(ind).LogText);
ind := le.NEXT(ind);
end loop;
end if;
end loop;
-- Indicate that the job finished and detach from it.
dbms_output.put_line('Job has completed');
dbms_output.put_line('Final job state = ' || job_state);
dbms_datapump.detach(h1);
END;
The problem is that the export takes too long. It takes 25 minutes with this SQL Code. The Size of the schema is 1.8 GB.
And I would like to find out how much time individual steps take. That's why I want to insert a timestamp in after each process step. Then I can see how long individual steps need.
I have marked the code for the timestamp with (>>>> <<<<) in the code.
The timestamp is not updating the time. I need the CURRENT time after each process. Can you help me?
In your code, you are setting the value of v_systimestamp at the beginning of your script - this will not change throughout the script run. You can reset the value before you log like:
v_systimestamp TIMESTAMP := SYSTIMESTAMP
DBMS_OUTPUT.PUT_LINE(v_systimestamp)
or doing something like the following before and after each section you want to monitor (no variable required):
DBMS_OUTPUT.PUT_LINE('Time Started: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS'));
DBMS_OUTPUT.PUT_LINE('Time Ended: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS'));
Here example 'CURRENT_TIMESTAMP'
curDateTime TIMESTAMP := CURRENT_TIMESTAMP ;
DBMS_OUTPUT.PUT_LINE('CUR TIME '|| curDateTime);
If a procedure you run takes a long time, DBMS_OUTPUT.PUT_LINE won't help much. Well, it will display something, but - as you use a loop, depending on number of iterations, DBMS_OUTPUT might run out of buffer size or exceed number of visible lines in SQL*Plus (if you use it) and you'll lose part of the output.
Besides, you won't see a single letter of the DBMS_OUTPUT.PUT_LINE until the procedure finishes.
Therefore, I'd suggest you to use another approach - a simple logging which requires a table, a sequence and an (autonomous transaction) procedure. Here's the script:
CREATE TABLE a1_log
(
id NUMBER,
datum DATE,
descr VARCHAR2 (500)
);
CREATE SEQUENCE seqlognap START WITH 1 INCREMENT BY 1;
CREATE OR REPLACE PROCEDURE a1_p_log (par_descr IN VARCHAR2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO a1_log
SELECT seqlognap.NEXTVAL, SYSDATE, par_descr FROM DUAL;
COMMIT;
END a1_p_log;
Then put A1_P_LOG calls into your own procedure; something like
begin
a1_p_log('selecting from a large table');
select ... into ... from ...;
a1_p_log('entering the loop');
while ... loop
a1_p_log('doing something');
end loop;
a1_p_log('the end');
end;
Here's where the autonomous transaction matters - it'll commit inserts into the log table (without affecting the main transaction), so you can (in another session, of course) trace the execution, simply by repeatedly issuing
SELECT *
FROM a1_log
ORDER BY id;
It'll show (imaginary example):
1 22.05.2018 18:13:00 selecting from a large table
2 22.05.2018 18:20:07 entering the loop
3 22.05.2018 18:20:07 doing something
4 22.05.2018 18:20:08 doing something
5 22.05.2018 18:20:09 doing something
6 22.05.2018 18:20:10 the end
and you'll see that step 1 takes 7 minutes to execute, so - that's what you need to investigate. It means that you don't have to wait the main procedure to finish - cancel it and start working on the bottleneck. Once you fix it, run everything again.
Related
I am currently implementing a PL/SQL procedure, which balances a value in two lists.
Consider this example as background:
Rec_1 | 2
Rec_2 | 1
Rec_3 | 2
Rec_A | -1
Rec_B | -3
Rec_C | -2
I want to loop through all these values one-by-one and settle as much as possible, i.e. that after the first settlement Rec_1 should be 1, Rec_A should be 0. Afterwards, Rec_1 will be settled with Rec_B such that it gets 1, Rec_B gets -2, and so on.
I want to use two cursors to do this, and update the values in its own procedure (or function, if that is necessary), since there is a little more to do than just update this value.
Now, here is my challenge:
How do I know which cursor to fetch after a settlement has happened?
Right now, my code for this function looks like this:
PROCEDURE SettleLists (
ListNegV SYS_REFCURSOR,
ListPosV SYS_REFCURSOR
) IS
currentNegV TABLENAME%ROWTYPE;
currentPosV TABLENAME%ROWTYPE;
BEGIN
FETCH ListNegV INTO currentNegV;
FETCH ListPosV INTO currentPosV;
LOOP
EXIT WHEN ListNegV%NOTFOUND;
EXIT WHEN ListPosV%NOTFOUND;
IF (currentNegV.NUMERICVALUE < 0)
THEN
IF (currentPosV.NUMERICVALUE > 0)
THEN
Settle(currentPosV, currentNegV);
ELSE
FETCH ListPosV INTO currentPosV;
END IF;
ELSE
FETCH ListNegV INTO currentNegV;
END IF;
END LOOP;
END;
Inside the settle procedure, there will be an UPDATE on both records. Since the variables and cursor values are not updated, this will produce an infinite loop. I could update the parameter of settle when the record is updated in the database as well, but since I am not used to cursors, you might have a better idea.
I could consider the cursor to be strongly typed, if that makes any difference. If there is a better way than using a cursor, feel free to suggest it.
Finally, I was playing around with SELECT FOR UPDATE and UPDATE WHERE CURRENT OF, but it did not seem to work when passing the cursor to a procedure in between. If anyone has some idea on this, I would also appreciate your help.
Here is a what I would do. I will surely add some comments.
This is running fine in Oracle 11Gr2 and I am hoping it will run fine even in 7i :).
declare
cursor c1 is
select 'REC_1' HEader,2 Val from dual
union
select 'REC_2' HEader,1 Val from dual
union
select 'REC_3' HEader,2 Val from dual;
cursor c2 is
select 'REC_A' HEader,-1 Val from dual
union
select 'REC_B' HEader,-3 Val from dual
union
select 'REC_C' HEader,-2 Val from dual;
num_bal1 number;
num_bal2 number;
num_settle_amt number;
rec_type_c1 c1%rowtype;
rec_type_c2 c2%rowtype;
begin
Open c1;
open c2;
fetch c1 into rec_type_c1;
fetch c2 into rec_type_c2;
num_bal1 := nvl(rec_type_c1.val,0);
num_bal2 := rec_type_c2.val;
Loop
exit when c1%notfound or c2%notfound;
Loop
dbms_output.put_line('Processing ' || rec_type_c1.header || ' with ' || num_bal1);
--In your example there are only +ve for 1 and -ve for 2. But if that is not correct, check for signs and next 3 statements
num_settle_amt := least(abs(num_bal1), abs(num_bal2) );
num_bal1 := num_bal1 - num_settle_amt;
num_bal2 := num_bal2 + num_settle_amt;
dbms_output.put_line('Setteled ' || num_settle_amt || ' of ' || rec_type_c1.header || ' with ' || rec_type_c2.header );
if num_bal1 = 0 then
--Update in the table. It will not impact variable.
fetch c1 into rec_type_c1;
num_bal1 := nvl(rec_type_c1.val,0);
end if;
if num_bal2 = 0 then
--Update in the table. It will not impact variable.
fetch c2 into rec_type_c2;
num_bal2 := nvl(rec_type_c2.val,0);
end if;
End loop;
end loop;
close c1;
close c2;
end;
I am new to PLSQL. I am trying to create a procedure which iterates through an array.
My requirement is if one of the value is not found in table, it should add into FAILARRAY, otherwise it should add into PASSARRAY.
I was getting no data found exception even if it is handled, it goes out of the loop and next value in the loop is not getting iterated again.
Is there any way we can use if exists command here. Please help.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE
(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY,
)
IS
--DECLARE
fileName VARCHAR2 (50);
fileId NUMBER;
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
end loop
END;
I suspect you haven't realised that you can have a PL/SQL BEGIN ... END block, including an exception handler, within a loop. In fact, anywhere you can have PL/SQL statements you can have a block.
You mention an exception handler, although your code doesn't contain one. As you say your code goes 'out of the loop', I can only assume it's, well, outside of the for loop. But you can easily add a block, with an exception handler, inside the for loop, for example:
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
-- Inner block starts at the line below:
BEGIN
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
-- TODO add to PASSARRAY
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- TODO add to FAILARRAY
END;
end loop
END;
This way, if there are 8 values in FILEARRAY and no data is found in the table for the third value, the NO_DATA_FOUND exception gets caught without exiting the loop and the loop then progresses to the fourth value in FILEARRAY.
You are handling the exception but you need to avoid the exception. Try:
SELECT NVL(FILEID, "<Put Something here or leave it empty") FROM TABLE_NAME WHERE FILENAME=fileName;
That way if it finds a null value in the select it will just pull "" instead. Then you can check to see if your SELECT returns "" and if so populate your FAILARRAY, otherwise populate PASSARRAY.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY )
IS
fileName VARCHAR2 (50);
l_n_count NUMBER;
l_n_file_id NUMBER;
BEGIN
FOR i IN 1 .. FILEARRAY.count
LOOP
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY(i));
SELECT COUNT(FILEID) INTO l_n_count FROM TABLE_NAME WHERE FILENAME=fileName;
IF l_n_count =0 THEN
failarray(i):='No Value Found';
elsif l_n_count=1 THEN
SELECT FILEID INTO l_n_file_id FROM TABLE_NAME WHERE FILENAME=fileName;
Passarray(i):=l_n_file_id;
END IF;
END LOOP;
END;
/
I have the following problem. I'm trying to check a number (bsn), if it's in the database or not. If it's not in the database it should give me an error, however now I'm getting always an error even if the number exists in the database. It worked fine with only one number in the database, but with more... That's the problem. Oh and I'm working with APEX, so I use this as a process.
create or replace PROCEDURE CONTROLE_BSN IS
CURSOR c_klanten
IS
SELECT bsn
FROM klant;
v_bsn VARCHAR2(10) := V('P7_BSN');
e_geen_bsn EXCEPTION;
BEGIN
FOR r_record IN c_klanten
LOOP
IF r_record.bsn != v_bsn THEN
RAISE e_geen_bsn;
END IF;
END LOOP;
EXCEPTION
WHEN e_geen_bsn THEN
raise_application_error(-20001, 'This bsn-number does not exists.');
END CONTROLE_BSN;
Your logic is flowed. As soon as you have two different bsn in your table, your test will be true for at least one of them:
FOR r_record IN c_klanten
LOOP
IF r_record.bsn != v_bsn THEN --< when N different records,
-- this is true for at least N-1 of them
RAISE e_geen_bsn;
END IF;
END LOOP;
Maybe you should go for something a little bit simpler than that. Why not write your cursor like this instead:
CURSOR c_klanten
IS
SELECT count(*) n
FROM klant
WHERE nbc = v_bsn;
That way, you will easily get the number of matching bsn. Either 0, 1 or more. And then perform the appropriate action.
Perhaps the following would help:
create or replace PROCEDURE CONTROLE_BSN IS
CURSOR c_klanten(p_bsn) IS
SELECT count(*) as bsn_count
FROM klant
where bsn = p_bsn;
v_bsn VARCHAR2(10) := V('P7_BSN');
e_geen_bsn EXCEPTION;
BEGIN
FOR r_record IN c_klanten(v_bsn)
LOOP
IF r_record.bsn_count = 0 THEN
RAISE e_geen_bsn;
END IF;
END LOOP;
EXCEPTION
WHEN e_geen_bsn THEN
raise_application_error(-20001, 'This bsn-number does not exists.');
END CONTROLE_BSN;
Best of luck.
I have a parameter table with 10 rows. Called parameter_table.
In my PL/SQL procedure, I do loop in 2 million records. And each time querying this parameter table too.
I want to load this parameter table in to the memory and decrease the I/O process.
What is the best way to do this?
FOR cur_opt
IN (SELECT customer_ID,
NVL (customer_type, 'C') cus_type
FROM invoice_codes
WHERE ms.invoice_type='RT')
LOOP
....
...
Select data From parameter_table Where cus_type = cur_opt.cus_type AND cr_date < sysdate ; -- Where clause is much complex than this..
....
...
END LOOP;
You can just join it to your main query:
select customer_id, data
from parameter_table t, invoice_codes c
where t.cus_type = nvl(c.customer_type, 'C')
and t.cr_date < sysdate
However, if you've got 2 million records in invoice_codes, then joining to the parameter table is the least of your concerns - looping through this will take some time (and is probably the real cause of your I/O problems).
I Think you may change the query ,joining to parameter_table, so there will be no need to hit the select statement inside the loop. (like what #Chris Saxon solution)
But as a way to use cashed data,
You could fill a dictionary like, array and then refer it when necessary
Something like this may help:
you have to call Fill_parameters_cash before starting the main process and call get_parameter to fetch the data, the input parameter to call get_parameter is the dictionary key
TYPE ga_parameter_t IS TABLE OF parameter_table%ROWTYPE INDEX BY BINARY_INTEGER;
ga_parameter ga_parameter_t;
procedure Fill_parameters_cash is
begin
ga_parameter.DELETE;
SELECT * BULK COLLECT
INTO ga_parameter
FROM parameter_table;
end Fill_parameters_cash;
FUNCTION get_parameter(cus_type invoice_codes.cus_type%TYPE,
is_fdound OUT BOOLEAN)
RETURN parameter_table%ROWTYPE IS
result_value parameter_table%ROWTYPE;
pos NUMBER;
BEGIN
result_value := NULL;
is_fdound := FALSE;
IF cus_type IS NULL THEN
RETURN NULL;
END IF;
pos := ga_parameter.FIRST;
WHILE pos IS NOT NULL
LOOP
EXIT WHEN ga_parameter(pos).cus_type = cus_type;
pos := ga_parameter.NEXT(pos);
END LOOP;
IF pos IS NOT NULL THEN
is_fdound := TRUE;
result_value := ga_parameter(pos);
END IF;
RETURN result_value;
END get_parameter;
I'd guess looping through a million records is already causing issues. Not quite sure how this parameter table lookup is really worsening it.
Anyways, if this is really the only approach you can take, then you could do an inner or outer join in the cursor declaration.
----
FOR cur_opt
IN (SELECT customer_ID,
NVL (customer_type, 'C') cus_type
FROM invoice_codes codes,
parameter_table par
WHERE ms.invoice_type='RT'
and codes.cus_type = par.cus_type -- (or an outer join) maybe?
) loop
..........
This is going to be a difficult question to get answered which is why for 3 days that I have worked on this package (my first package ever) I have been hesitant to ask.
Below is the layout for the spec and body of my package. Before you look at that here is what I am trying to accomplish. I AM CLOSE TO FINISHING so there is no need to fear that this question is not worth your time.
You may see a few of my personal notes to self in the code as well.
My code is incomplete and currently isn't compiling but before it ceased to compile I can tell you it did not work either. The DROP and CREATE procedures work. NO NEED TO TOUCH THOSE. My main issues are the LOG_PROC, my EXCEPTIONS, my ARCHIVE_ALL_TABLES... as far as I know
Here is what I am trying to do:
Create a package that could be used to ‘archive’ the newly created tables into archive tables in the format “TEST_TABLE_A_13AUG2012”. This package will use a view I created called VW_TEST_TABLES which has this data:
TEST_TABLE_A
TEST_TABLE_B
TEST_TABLE_C
TEST_TABLE_D
This package will need to drop all previously archived tables before it creates new ones. As such, my package will need to have both DROP_ARCHIVE_TABLES and CREATE_ARCHIVE_TABLES procedures within it. In addition to the DROP and CREATE procedures, my package has a main procedure, called ARCHIVE_ALL_TABLES. This is the procedure that would need to be called (for instance by the scheduler) and do the actual archiving. I need to incorporate proper exception handling in these procedures. (e.g. don’t care if the table does not exist when I go to drop it).
Finally, in order to properly track each archival run, I want to build a logging mechanism. To accomplish this, I built a table in my schema called TEST_PACKAGE_LOG_TBL. This table should has the following columns: ARCHIVE_DATE (DATE), TABLE_NAME (VARCHAR2(30)), STATUS_CODE(VARCHAR2(1)), COMMENTS (VARCHAR2(4000)). For each table I archive, I want to log the date, the table name, either ‘S’ for success or ‘E’ for error and, if I encounter an error in the drop or creation of the table, what the SQLERRM was should be displayed.
Finally, my ARCHIVE_ALL_TABLES procedure should check this log table when it is finishing in order to determine if any tables were not archived properly. I created a function ERRORS_FOUND (return boolean) that accepts one IN parameter (today’s date) and checks the log table for errors. If this function returns true, my ARCHIVE_ALL_TABLES procedure should account for this and ‘notify an administrator’ (For now I am leaving this untouched but eventually it will simply account for this with a comment stating that I would notify an admin and place NULL; in the if then end block.)
To summarize, my package structure must contain (at minimum) the following procedures:
ARCHIVE_ALL_TABLES,
DROP_ARCHIVE_TABLE,
CREATE_ARCHIVE_TABLE,
ERRORS_FOUND (function)
--package specification
CREATE OR REPLACE PACKAGE PKG_TEST_TABLES IS
-- Author :
-- Created : 8/14/2012 8:40:18 AM
-- Purpose : For storing procedures to drop, create, and archive new tables
/* Package specification*/
PROCEDURE ARCHIVE_ALL_TABLES;
PROCEDURE DROP_ARCHIVE_TABLES; --2nd
PROCEDURE CREATE_ARCHIVE_TABLES; --1st and call both from archive tables first assuming it works
PROCEDURE LOG_PROC
(
P_PROCESS_START_TIMESTAMP TIMESTAMP
,P_ARCHIVE_DATE DATE
,P_TABLE_NAME VARCHAR2
,P_STATUS_CODE VARCHAR2
,P_COMMENTS VARCHAR2
);
PROCEDURE W(STR VARCHAR2);
FUNCTION ERRORS_FOUND(P_JOB_RUN_TIMESTAMP TIMESTAMP) RETURN BOOLEAN;
END PKG_TEST_TABLES;
--package body
CREATE OR REPLACE PACKAGE BODY PKG_TEST_TABLES IS
/* Package body*/
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
/* Procedure 'W' is a wrapper for DBMS output. Placed at top of package to make globally available*/
PROCEDURE W(STR VARCHAR2) IS
L_STRING VARCHAR2(4000);
BEGIN
L_STRING := STR;
DBMS_OUTPUT.PUT_LINE(STR);
END;
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
PROCEDURE DROP_ARCHIVE_TABLES AS
/* Purpose: For dropping previously archived tables so that new ones can be created */
L_NO_TABLES_TO_DROP EXCEPTION;
BEGIN
/* Will drop previously archived tables not current ones*/
FOR STMT IN (SELECT 'DROP TABLE mySchema.' || TABLE_NAME AS STR
FROM VW_TEST_TABLES
WHERE REGEXP_LIKE(TABLE_NAME, '.+[0...9]'))
LOOP
EXECUTE IMMEDIATE STMT.STR; --so that I don't need ';' at the end of each dynamically created SQL
END LOOP;
W('Done'); --put the W back in here when in package scope
EXCEPTION
WHEN L_NO_TABLES_TO_DROP THEN
NULL;
END;
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
PROCEDURE CREATE_ARCHIVE_TABLES AS
/* purpose: setting variable to equal the creation of my 4 tables. Recreating the archive tables */
L_NO_TABLES_TO_CREATE EXCEPTION;
L_TABLES_NOT_SUCCESSFULLY_CREATED EXCEPTION;
BEGIN
FOR STMT IN (SELECT 'CREATE TABLE ' || TABLE_NAME || '_' || TO_CHAR(SYSDATE, 'ddMONyyyy') || ' AS SELECT * FROM ' || TABLE_NAME AS STR
FROM VW_TEST_TABLES)
--LOG_PROC( ,TO_CHAR(SYSDATE, 'ddMONyyyy') , TABLE_NAME ,'E' ,'TABLE ARCHIVED SUCCESSFULLY')
LOOP
--DBMS_OUTPUT.PUT_LINE(STMT.STR); --want to do a dbms output first before using 'execute immediate'. Hit test, and run it
EXECUTE IMMEDIATE STMT.STR; --so that I don't need ';' at the end of each dynamically created SQL
END LOOP;
-- DBMS_OUTPUT.PUT_LINE('Done'); --put the W back in here when in package scope
EXCEPTION
WHEN L_NO_TABLES_TO_CREATE THEN
NULL; --logging can go here
--can call logging procedure here for dml don't need execute immediate, just use insert into
WHEN L_TABLES_NOT_SUCCESSFULLY_CREATED THEN
NULL; --W('ERROR: ' || SQLERRM);
END;
--PROCEDURE IS NOT CREATING TABLES YET
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
PROCEDURE LOG_PROC(P_PROCESS_START_TIMESTAMP TIMESTAMP, P_ARCHIVE_DATE DATE, P_TABLE_NAME VARCHAR2, P_STATUS_CODE VARCHAR2, P_COMMENTS VARCHAR2) AS
PRAGMA AUTONOMOUS_TRANSACTION;
/* variables */
L_PROCESS_START_TIMESTAMP TIMESTAMP; L_ARCHIVE_DATE DATE; L_TABLE_NAME VARCHAR2(4000); L_STATUS_CODE VARCHAR2(1); L_COMMENTS VARCHAR2(4000);
BEGIN
L_PROCESS_START_TIMESTAMP := P_PROCESS_START_TIMESTAMP; L_ARCHIVE_DATE := P_ARCHIVE_DATE; L_TABLE_NAME := P_TABLE_NAME; L_STATUS_CODE := P_STATUS_CODE; L_COMMENTS := P_COMMENTS;
INSERT INTO TEST_PACKAGE_LOG_TBL(PROCESS_START_TIMESTAMP, ARCHIVE_DATE, TABLE_NAME, STATUS_CODE, COMMENTS) VALUES(L_PROCESS_START_TIMESTAMP, L_ARCHIVE_DATE, L_TABLE_NAME, L_STATUS_CODE, L_COMMENTS);
RETURN;
END;
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
FUNCTION ERRORS_FOUND(P_JOB_RUN_TIMESTAMP TIMESTAMP) RETURN BOOLEAN IS
L_JOB_RUN_TIMESTAMP TIMESTAMP; ERROR_COUNT NUMBER; ERROR_BOOL BOOLEAN;
BEGIN
L_JOB_RUN_TIMESTAMP := P_JOB_RUN_TIMESTAMP;
SELECT COUNT(*) INTO ERROR_COUNT FROM TEST_PACKAGE_LOG_TBL WHERE STATUS_CODE = 'E' AND PROCESS_START_TIMESTAMP = L_JOB_RUN_TIMESTAMP; IF ERROR_COUNT > 0 THEN ERROR_BOOL := TRUE; ELSE ERROR_BOOL := FALSE;
END IF;
RETURN ERROR_BOOL;
END;
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
PROCEDURE ARCHIVE_ALL_TABLES AS
/*
Original Author:
Created Date: 13-Aug-2012
Purpose: To drop all tables before recreating and archiving newly created tables
NOTE: in package - do not use create or replace and 'as' would be alternative to 'is'
*/
/*variables*/
L_DROP_ARCHIVE_TABLES VARCHAR2(4000); L_SQL_CREATE_ARCHIVED_TABLES VARCHAR2(4000); L_PREVENT_SQL_INJECTION
EXCEPTION
;
--L_NOTIFY_ADMINISTRATOR VARCHAR(4000); --TO BE DONE AT A LATER TIME
BEGIN
RETURN;
EXCEPTION
WHEN L_PREVENT_SQL_INJECTION THEN NULL;
WHEN OTHERS THEN W('ERROR: ' || SQLERRM);
END;
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
------------------------------------------------------------------------------------------------- ------------------------------------------------------------------
BEGIN
-- Initialization
/*archive all tables is like my 'driver' that calls drop then create while logging to the table. Pragma_auto prevents a rollback which would prevent table logging
FIRST: This package will need to drop all previously archived tables before it creates new ones. call drop func first*/
/* calling ARCHIVE_ALL_TABLES */
BEGIN
-- Call the function
NULL;
END;
RETURN;
END PKG_TEST_TABLES;
Your LOG_PROC is an autonomous transaction, so you need a COMMIT in there.
You define a number of exceptions, but you don't RAISE them anywhere in your code. For example, I'm guessing you need something like this:
PROCEDURE CREATE_ARCHIVE_TABLES AS
L_NO_TABLES_TO_CREATE EXCEPTION;
l_count number := 0;
BEGIN
FOR STMT IN (SELECT ...)
LOOP
l_count := l_count + 1;
EXECUTE IMMEDIATE STMT.STR;
END LOOP;
IF l_count = 0 THEN
RAISE L_NO_TABLES_TO_CREATE;
END IF;
EXCEPTION
WHEN L_NO_TABLES_TO_CREATE THEN
NULL; --logging can go here
END;