Infinite job execution locking table - oracle

Here is the query:
INSERT INTO resumo
(tipo, id_tipo, qtde)
SELECT tipo, id_tipo, COUNT (*) qtde
FROM tabela
WHERE id_tipo > 400
GROUP BY tipo, id_tipo;
COMMIT;
If I run it manually, it took like 30 seconds to execute.
If I put the same query into a package and create a job to run it, it just keeps running forever, locking the table resumo. Any ideas why this happens??
It's oracle 11g.
EDIT
Package Spec:
CREATE OR REPLACE PACKAGE PKG_TESTE AS
procedure geraTabela;
End PKG_TESTE;
/
Package Body:
CREATE OR REPLACE PACKAGE body PKG_TESTE AS
procedure geraTabela is
begin
INSERT INTO resumo
(tipo, id_tipo, qtde)
SELECT tipo, id_tipo, COUNT (*) qtde
FROM tabela
WHERE id_tipo > 400
GROUP BY tipo, id_tipo;
COMMIT;
end;
end PKG_TESTE;
/
Job:
DECLARE
X NUMBER;
BEGIN
SYS.DBMS_JOB.SUBMIT
( job => X
,what => 'PKG_TESTE.geraTabela;'
,next_date => to_date('18/05/2016 11:00:00','dd/mm/yyyy hh24:mi:ss')
,interval => 'SYSDATE+60/1440 '
,no_parse => FALSE
);
COMMIT;
END;
/

Related

Committing concurrent transactions only if all of them succeed

What I want is to execute a procedure ( that will run on Oracle 11g) concurrently, and to commit the whole operation if an only if all the concurrent transactions succeeded.
Two ways of parallel execution that I thought of were DBMS_PARALLEL_EXECUTE and dbms_job.submit(), but as I understand it, in both cases the processes created are running in their separate sessions and each process commits the changes upon termination (or in case of an error can rollback its own changes).
What I would like, is to start parallel processes, wait until each one of them is finished, check if they all were successful and only then commit the changes (or rollback if at least one process failed).
Is the above scenario possible? (And how can it be implemented)
Thanks.
I am curious why this requirement came about; I would probably question whether it was really necessary if this came to me. But if you cannot question the requirement, this is how I would go about it.
-- We need to have a common persistent location for all of the jobs to read
CREATE TABLE test_tab
(
job_name VARCHAR2(30),
status VARCHAR2(30)
)
/
-- The procedure writing to our table must be autonomous so that updates occur
-- without committing the rest of the work
CREATE OR REPLACE PROCEDURE test_log
(
i_job_name IN VARCHAR2,
i_status IN VARCHAR2
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
MERGE INTO test_tab tgt
USING dual
ON (tgt.job_name = i_job_name)
WHEN MATCHED THEN
UPDATE SET status = i_status
WHEN NOT MATCHED THEN
INSERT VALUES (i_job_name, i_status);
COMMIT;
END test_log;
/
CREATE OR REPLACE PROCEDURE test_proc(i_job_name IN VARCHAR2) IS
l_complete_cnt INTEGER;
l_error_cnt INTEGER;
l_waiting BOOLEAN := TRUE;
BEGIN
-- !!! Your code here !!!
/* -- Uncomment this block to prove the rollback scenario.
IF i_job_name LIKE '%8' THEN
raise_application_error(-20001, 'Throwing an error to prove rollback.');
END IF;*/
test_log(i_job_name, 'COMPLETE');
WHILE l_waiting LOOP
SELECT SUM(CASE WHEN status IN ('COMPLETE', 'COMMITTED') THEN 1 ELSE 0 END)
,SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END)
INTO l_complete_cnt
,l_error_cnt
FROM test_tab
WHERE REGEXP_LIKE(job_name, 'TEST_JOB_\d');
IF l_complete_cnt = 8 THEN
COMMIT;
test_log(i_job_name, 'COMMITTED');
l_waiting := FALSE;
ELSIF l_error_cnt > 0 THEN
ROLLBACK;
test_log(i_job_name, 'ROLLBACK');
l_waiting := FALSE;
ELSE
dbms_lock.sleep(seconds => 5);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
test_log(i_job_name, 'ERROR');
RAISE;
END;
/
-- Begin test section
BEGIN
FOR i IN 1..8 LOOP
dbms_scheduler.create_job('TEST_JOB_'||i
,'PLSQL_BLOCK'
,'BEGIN test_proc(''TEST_JOB_'||i||'''); END;'
,start_date => SYSDATE
,enabled => TRUE);
END LOOP;
END;
/
SELECT * FROM test_tab;
TRUNCATE TABLE test_tab; --the table should be cleared between tests

Oracle lookup function in loop returns rowtype how to get fields from rowtype

I have a procedure that runs a select ( I tested that is good returns 56 records )
then when I run a cursor I want to pass 3 fields to a function ( see above ) that will
lookup/select a record from a table that contains 15 million records ( 10 years worth ).
It returns a rowtype that I want to then extract the fields from this rowtype record to
run an insert with both the records from the 1st select and the additional fields acquired
from the lookup function.
If I run the procedure the console prints out my test msgs but when I try to run
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
it doesn't compile due to Error(97,65): PL/SQL: ORA-00942: table or view does not exist
as if either of these are not Tables.
Am I doing this right... how can I do it, why can't it see the table I'm trying to extract to ?
Should I do this some other way..
Thanks for any help/suggestions :)
The function is below....
create or replace function get_processed_claim_rec(
p_provider VARCHAR2,
p_rx VARCHAR2,
p_record_no NUMBER
)
return i_idb.processed_claim%rowtype
as
l_claim_record i_idb.processed_claim%rowtype;
begin
select * into l_claim_record from i_idb.processed_claim
where source_date = p_provider
AND rx = p_rx
AND rec_no = p_record_no;
return(l_claim_record);
end;
And the procedure is....
create or replace PROCEDURE import_mailer_data
AS
-------------------------------
/**
for the lookup table
**/
v_processed_claim i_idb.processed_claim%rowtype;
TYPE proc_claim_recs IS TABLE OF v_processed_claim%ROWTYPE INDEX BY PLS_INTEGER;
v_tab_proc_claim_recs proc_claim_recs;
--------------------------------
CURSOR myCursor
IS
SELECT *
from
(
SELECT
j.create_date as open_date,
case when (j.create_date < (sysdate - 20) )
then 'POD'
else 'REG'
end as priority,
c.division,
c.unit,
--p.refill as days_supply,
--p.din_name,
'CM_JOHN' as log_code,
c.first_name,
c.last_name,
--p.UNLISTED_compound,
--p.intervention_code,
--p.substitution,
--p.confirm,
c.PROVIDER,
c.rx,
c.DISPENSE_DATE,
c.DIN,
c.QTY,
c.DIN_COST_PAID,
c.DISP_FEE_PAID,
c.PAID_AMOUNT,
c.SOURCE_DATE,
c.RECORD_NO,
c.RELATIONSHIP,
c.INSURER_NO,
c.GROUP_NO,
c.CERTIFICATE,
c.BIRTH_DATE,
c.USER_ID,
--p.rej_code --v_seq_no
rank() over
(
partition by c.provider, c.rx, c.record_no Order by c.provider desc, c.rx desc
) as RNK
FROM AUDITCOLLECTIONS.MAILER_CLAIMS c,
AUDITCOLLECTIONS.MAILER_JOBS j
WHERE MAILER_JOB_DETAIL_ID IN
(SELECT MAILER_JOB_DETAIL_ID
FROM AUDITCOLLECTIONS.MAILER_JOB_DETAILS
WHERE MAILER_JOB_ID IN
( SELECT MAILER_JOB_ID FROM AUDITCOLLECTIONS.MAILER_JOBS
)
)
AND ( c.PROVIDER, c.rx, c.record_no ) NOT IN
( SELECT provider, rx, rec_no FROM AUDITCOLLECTIONS.COLLECTION_AUDIT_STAGING
)
AND j.create_date > (sysdate - 30)
AND c.provider = '2010500042'
) A_Latest
where A_Latest.RNK = 1;
BEGIN
v_report_id := audit_load.create_loaded_report(v_report_type_id);
FOR curRec IN myCursor
LOOP
BEGIN
dbms_output.put_line ('===>>>> PRINTING TEST1 = ');
v_processed_claim := get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
END LOOP;
audit_load.update_status_to_loaded(v_report_id);
END import_mailer_data;
You can do this:
FOR curRec IN myCursor
LOOP
v_processed_claim :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) := v_processed_claim;
END LOOP;
Or simplify to:
FOR curRec IN myCursor
LOOP
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
END LOOP;

Trigger to disable insert into a table for a specific day in oracle

How can i correctly write a trigger for this task:
create or replace trigger M_t2
after insert on emp
begin
if ( to_char(sysdate,'DY' ) = 'TUE' ) then
dbms_output.put_line('cannot insert into emp on tuesday');
end if;
end;
/
This does not work as i am still able to insert like this:
insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values('7935','BOLT','ANALYST',7698,sysdate,900,100,10);
dbms_output doesn't stop you doing anything, and you won't even see that message if your client isn't set up to show the output.
To prevent an action you'd need to raise an exception:
if ( to_char(sysdate,'DY' ) = 'TUE' ) then
raise_application_error(-20001, 'cannot insert into emp on tuesday');
end if;
The DY value is NLS-dependent so this could be circumvented by having a session with a different language, so you should take that into account using the optional third parameter to to_char(). It might as well be a before-insert trigger too:
create or replace trigger M_t2
before insert on emp
begin
if ( to_char(sysdate, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH' ) = 'TUE' ) then
raise_application_error(-20001, 'cannot insert into emp on tuesday');
end if;
end;
/
insert into emp ...
ERROR at line 1:
ORA-20001: cannot insert into emp on tuesday
ORA-06512: at "<schema>.N_T2", line 3
ORA-04088: error during execution of trigger '<schema>.M_T2'
Please look at this: http://www.techonthenet.com/oracle/triggers/after_insert.php
I think you must use :new clause and use variable

Oracle scheduled job fails

I am using Oracle 10g and using following script to create the job
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
The job never executes automatically (though it runs manually) with following error in alert_sid.log
ORA-12012: error on auto execute of job 26
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 8
I am unable to link the ORA-01422 error with any of my code. I'm not doing any fetch here.
Assuming this is a script for SQL*Plus, there are two / misssing, so it does nothing at all:
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
/
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
/
I guess it's another job failing, not yours.
You don't do any data fetch here, but I guess some ON UPDATE trigger on ARCH_TEMP table might. Check it.
I'd use a SERVERERROR trigger (as described here) to try to catch the statement that is failing. But first, you could check the alert log. If recursive SQL is erroring, there may be a problem in the data dictionary.
Try putting in an explicit PL/SQL block as the WHAT parameter.
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
Here's my test case, which seems to work fine:
create table arch_temp (
arch_date date
);
-- create row to test update
insert into arch_temp (arch_date) values (null);
create or replace procedure archtemp as
begin
update arch_temp set arch_date = sysdate;
commit;
end archtemp;
/
-- test everything works in isoloation
begin
archtemp;
end;
/
select * from arch_temp;
-- arch_date = 10:49:34
select * from user_jobs;
-- no rows returned
declare
v_jobno number;
begin
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
commit;
dbms_output.put_line('v_jobno: ' || to_char(v_jobno));
end;
/
-- dbms_output...
-- v_jobno: 50520
select * from user_jobs;
-- JOB 50520 returned
-- LAST_DATE = 10:51:11
select * from arch_temp;
-- ARCH_DATE = 10:51:11
I tried solution by Nick Pierpoint as well but it didn't work for me
It looks something is wrong with LUCK because i tried the same thing on another machine having Oracle 9i and it failed!!!
Thank you all for your replies.
Regards

How to develop an after serverror trigger in Oracle?

I'm trying to log all the errors in my database into a table. So as user sys i wrote the following code:
CREATE TABLE servererror_log (
error_datetime TIMESTAMP,
error_user VARCHAR2(30),
db_name VARCHAR2(9),
error_stack VARCHAR2(2000),
captured_sql VARCHAR2(1000));
/
CREATE OR REPLACE TRIGGER log_server_errors
AFTER SERVERERROR
ON DATABASE
DECLARE
captured_sql VARCHAR2(1000);
BEGIN
SELECT q.sql_text
INTO captured_sql
FROM gv$sql q, gv$sql_cursor c, gv$session s
WHERE s.audsid = audsid
AND s.prev_sql_addr = q.address
AND q.address = c.parent_handle;
INSERT INTO servererror_log
(error_datetime, error_user, db_name,
error_stack, captured_sql)
VALUES
(systimestamp, sys.login_user, sys.database_name,
dbms_utility.format_error_stack, captured_sql);
END log_server_errors;
But when i force an error like trying to select from a non-existing table it doesn´t log the error in the table.
Is there any way to check that the trigger fires at all? Also, I tried creating a test table to insert there but it doesn't work either, even if a define the trigger as an autonomous transaction and commit inside the trigger.
Thanks,
Joaquin
Do not query v$sql; get the statement using ora_sql_txt.
CREATE OR REPLACE TRIGGER log_server_errors
AFTER SERVERERROR
ON DATABASE
DECLARE
sql_text ora_name_list_t;
stmt clob;
n number;
BEGIN
n := ora_sql_txt(sql_text);
if n > 1000 then n:= 1000; end if ;
FOR i IN 1..n LOOP
stmt := stmt || sql_text(i);
END LOOP;
INSERT INTO servererror_log
(error_datetime, error_user, db_name,
error_stack, captured_sql)
VALUES
(systimestamp, sys.login_user, sys.database_name,
dbms_utility.format_error_stack, stmt);
commit;
END log_server_errors;
/
Then:
SQL> select * from c;
This produces:
select * from c
*
ERROR at line 1:
ORA-00942: table or view does not exist
That can now be queried:
select * from servererror_log;
To produce:
ERROR_DATETIME
---------------------------------------------------------------------------
ERROR_USER DB_NAME
------------------------------ ---------
ERROR_STACK
--------------------------------------------------------------------------------
CAPTURED_SQL
--------------------------------------------------------------------------------
11-FEB-09 02.55.35.591259 PM
SYS TS.WORLD
ORA-00942: table or view does not exist
select * from c
To see if the trigger is firing, add one or more lines to it like this:
DBMS_OUTPUT.PUT_LINE( 'Got this far' );
In SQLPlus, SET SERVEROUTPUT ON then execute a command to generate an error. You should get output like this:
dev> select * from aldfjh;
select * from aldfjh
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-00942: table or view does not exist
Got this far
Check the status of your trigger and/or the existence of other triggers with:
select trigger_name, status
from all_triggers
where triggering_event like 'ERROR%'
This should result into:
TRIGGER_NAME STATUS
------------ -------
LOG_SERVER_ERRORS ENABLED
If trigger is not enabled or another trigger fails, it probably will not work.
Save this as ORA-00942.sql:
-- Drop trigger and ignore errors (e.g., not exists).
DECLARE
existential_crisis EXCEPTION;
PRAGMA EXCEPTION_INIT( existential_crisis, -4080 );
BEGIN
EXECUTE IMMEDIATE 'DROP TRIGGER TRG_CATCH_ERRORS /*+ IF EXISTS */';
EXCEPTION WHEN existential_crisis THEN
DBMS_OUTPUT.PUT_LINE('Ignoring non-existence.');
END;
/
-- Drop table and ignore errors (e.g., not exists).
DECLARE
existential_crisis EXCEPTION;
PRAGMA EXCEPTION_INIT( existential_crisis, -942 );
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE TBL_ERROR_LOG /*+ IF EXISTS */';
EXCEPTION WHEN existential_crisis THEN
DBMS_OUTPUT.PUT_LINE('Ignoring non-existence.');
END;
/
-- Create the table (will not exist due to drop statement).
CREATE TABLE TBL_ERROR_LOG (
occurred timestamp,
account varchar2(32),
database_name varchar2(32),
stack clob,
query clob
);
-- Create the trigger to log the errors.
CREATE TRIGGER TRG_CATCH_ERRORS AFTER servererror ON database
DECLARE
sql_text ora_name_list_t;
n number;
query_ clob;
BEGIN
n := ora_sql_txt( sql_text );
IF n > 1000 THEN n := 1000; END IF;
FOR i IN 1 .. n LOOP
query_ := query_ || sql_text( i );
END LOOP;
INSERT INTO TBL_ERROR_LOG
(occurred, account, database_name, stack, query)
VALUES
(systimestamp, sys.login_user, sys.database_name,
dbms_utility.format_error_stack, query_);
END;
/
Run using sqlplus:
SQL> #ORA-00942.sql
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
Table created.
Trigger created.
Test it:
select * from blargh;
select * from TBL_ERROR_LOG;
Output:
2017-10-20 15:15:25.061 SCHEMA XE "ORA-00942: table or view does not exist" select * from blargh

Resources