Skip to Main Content
  • Questions
  • Trigger to get updated order number and to pass the order number to a stored proc

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, blessings.

Asked: May 21, 2012 - 1:28 pm UTC

Last updated: May 23, 2012 - 6:14 pm UTC

Version: 10g

Viewed 1000+ times

You Asked

I have a senario where am trying to get the record of an updated order status and then pass on the order_number to a stored procedure. How can I do this? please see my trigger and stored proc:

I would want to update the analysis codes on nltranm, the table nltranm is updated when the status on poheadm is updated to 'C' i.e. Invoiced.
So I want to take the order number from this table and use it as input to the stored proc. The stored proc will be responsible for updating the table based on the conditions specified where the order number = @order_no(obtained from the trigger).

This is my objective:
When I capture I purchase order, I enter the description of the line details,
But unfortunately the system doesn't populate these descriptions in my general ledger, so am trying to come up with an automated solution to extract these descriptions from the original order to the g/L into 3 free analysis fields in the gl transaction table. There is no direct link between this table and the gl table, the link is achieved though linking several tables as shown in my insert query. I have established that the table pohead's status column is updated to 'C' when the transaction has been posted to the ledger, that is why I want to put my after update trigger there to get order number as input to the second part. How best can I achieve the above?

order
CREATE OR REPLACE TRIGGER trig_poheadm_invoiced
After update ON ucdem.poheadm
FOR EACH ROW
WHEN (NEW.STATUS = 'C')
DECLARE
order_no CHAR(10);
BEGIN
order_no := :NEW.order_no ;
sp_poheadm_invoiced(:NEW.order_no);

END;


CREATE PROCEDURE sp_poheadm_invoiced(new_order_no IN ucdem.poheadm.order_no%type) IS


begin

INSERT INTO manager.temp_nl_anals
(seq,
supplier,order_no, batch_number ,invoice,long_description,transaction_group,tran_code1,tran_code2,local_total_actual)


select e.seq,
a.supplier,b.order_no,SUBSTR(c.batch_number,1,6) as batch_number ,c.invoice,substr(b.long_description,1,10) as long_description,d.transaction_group,a.tran_code1,a.tran_code2,b.local_total_actual
from ucdem.podetm b, ucdem.poheadm a , ucdem.Poinvhm c, ucdem.plitemm d, ucdem.nltranm e where a.ORDER_NO = b.ORDER_NO
and b.line_type <> 'C'
and c.REFERNCE = b.order_no and c.supplier = a.supplier and c.REFERNCE = d.REFERNCE and c.supplier = c.supplier
and c.invoice = d.item and d.transaction_group = e.transaction_group
and substr(e.batch_reference,1,6) = substr(c.batch_number,1,6) AND substr(e.JOURNAL_NUMBER,1,6) = substr(c.invoice,1,6)
and b.local_total_actual = e.journal_amount
and b.order_no = new_order_no;




update (SELECT * FROM ucDEM.nltranm A , manager.temp_nl_anals B
where A.SEQ = B.SEQ )
SET analysis_code1 = tran_code1, analysis_code2 = tran_code2, analysis_code3 = long_description;


END;

and Tom said...

Ok, I don't like using triggers for stuff like this:

http://www.oracle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html

The simple fact is - there is a bug in the developed code and we are trying to hack a solution together to make things work as if by magic. It'll be very 'cool', it'll be very 'tricky'. It'll be something that bites us in the butt some day in the future.

I'll show you how I think this should be done if it is to be done in a trigger. We have two systems here. The order entry (OE) and general ledger (GL) systems. They are "loosely coupled" - two different applications. I don't think they should be tied together tightly with a trigger. We'll tie them together "loosely" with a trigger, a job, and a procedure.

first, we'll create a job parameter table:

create table trig_poheadm_invoiced_job ( job_no number primary key, order_no number ) organization index;


Next, you will write a procedure that given an Order_no - it does the right thing. You have that procedure SP_POHEADM_INVOICE. I'll assume that code is correct - even though it doesn't look like it is. That logic is logic you'll have to develop and debug. This procedure will look like this:

CREATE PROCEDURE sp_poheadm_invoiced( p_job in number
IS
    l_order_no number;
begin
    select order_no into l_order_no
      from trig_poheadm_invoiced_job
     where job_no = P_JOB;

    ... your logic ...

    delete from trig_poheadm_invoiced_job
     where job_no = P_JOB;
END;
/


Then we'll create our trigger:

CREATE OR REPLACE TRIGGER trig_poheadm_invoiced
After update ON ucdem.poheadm 
FOR EACH ROW 
WHEN (NEW.STATUS = 'C') 
DECLARE 
order_no CHAR(10);
BEGIN 
    dbms_job.submit( l_job, 'sp_poheadm_invoiced(JOB);' );

    insert into trig_poheadm_invoiced_job
    (job_no,order_no)
    values
    (l_job, :new.order_no);
END;
/




Now you have a loosely coupled, transactional interface that will safely move data from one place to another - without mutating table constraint issues and so on.

It'll also be easy to 'fix' the OE code by having it call the procedure directly later when you get around to fixing the code - if and when that happens, allowing you to drop the trigger.


Please make sure to use that interface table - the job table - do not pass the order_no directly in the job. This approach is bind friendly and makes debugging easy. Instead of deleting the row in the job table, you could update it with elapsed times, sysdate to show when it ran, error messages, whatever and keep a history.

Rating

  (7 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

I have flawed googling skils

Micheal Kutz, May 22, 2012 - 12:56 pm UTC

Tom,
Forgive me.
But, I can't seem to find anywhere in the Oracle Documentations anything that explains why and how 'sp_poheadm_invoiced(JOB);' doesn't throw a "PLS-00201: identifier 'JOB' must be declared" when the job runs.

Can you please show me which book and chapter I skipped-read/mis-read/un-read that contains that explanation?

Many Thanks,

Michael Kutz
MK
Tom Kyte
May 22, 2012 - 2:48 pm UTC

The block of code dbms_job submits is:

DECLARE 
   job BINARY_INTEGER := :job; 
   next_date DATE := :mydate;  
   broken BOOLEAN := FALSE; 
BEGIN
         WHAT
    :mydate := next_date; 
    IF broken THEN :b := 1; ELSE :b := 0; END IF; 
END;



where WHAT is whatever you put into the dbms_job.submit call. You have access to the JOB, NEXT_DATE and BROKEN variables.

The job is your job number, unique id.

If you set NEXT_DATE in your block of code - you can even tell us when to run the job next.

If you set broken, you can break yourself.


so, is it documented or not ?

Sokrates, May 22, 2012 - 5:07 pm UTC

It seems that Michael wants to know if this is documented or not.

That's a fair question in my eyes, because nobody should use an undocumented feature ( we all don't use SQL-function REVERSE for example).

It doesn't seem to be documented though, so probably it is not a good idea to follow your suggestion here ?
Tom Kyte
May 23, 2012 - 8:01 am UTC

I don't know that it is officially documented (short of Expert one on one Oracle) - however, it is the way it works - the way it has to work.

In this case, since this is quite simply the only way to make dbms_job "bind friendly", I'm more than OK with this.

It is easily observable and easy to verify that the block is consistent from release to release (it has been this way since dbms_job was first introduced)


Speaking of documentation of DBMS_JOB

Kim Berg Hansen, May 23, 2012 - 1:38 am UTC

The review of Sokrates reminded me of another thing concerning the documentation of DBMS_JOB.

One of the things that sometimes makes us choose DBMS_JOB rather than DBMS_SCHEDULER is that DBMS_JOB submission can be part of the transaction - if errors happen and you rollback then the job never happens - which you cannot (as far as I know) do with DBMS_SCHEDULER.

The documentation for DBMS_JOB.SUBMIT here:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_job.htm#i1000807

It states in the usage notes:

"You must issue a COMMIT statement immediately after the statement."

(Emphasis added by me.)

Is there a valid technical reason for the docs to be this categorical rather than simply stating that the job is part of the transaction and not truly submitted until you commit?

Or should we consider this a kind of "doc bug"? ;-)

Tom Kyte
May 23, 2012 - 8:50 am UTC

I would consider it a doc bug. but not a huge one - the word immediately should be removed. The word must may remain. The words "in order for the job to become visible to the job queue" should be added at the end.

And I encourage everyone to enter enhancement requests to make the scheduler transactional! (it is deficient in my opinion - and the sole reason I use dbms_job heavily is because the scheduler is NOT transactional)

dangerous

Sokrates, May 23, 2012 - 8:29 am UTC


... however, it is the way it works - the way it has to work. ...


It seems quite dangerous to use a feature just because we "know how it is implemented at the moment", doesn't it ?


... It is easily observable and easy to verify that the block is consistent from release to release (it has been this way since dbms_job was first introduced) ....


So does that mean, we have to update our Oracle-Upgrade-Procedure and add the following step to it:

"by the way, if you are using JOB, NEXT_DATE or BROKEN as bind variables in Parameter WHAT of DBMS_JOB.SUBMIT, please verify that this still does work in your new Oracle Version" ?

why can't they just update the Docs on
http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_job.htm#i1000807
so we can rely on this ?
Tom Kyte
May 23, 2012 - 10:56 am UTC

So does that mean, we have to update our Oracle-Upgrade-Procedure and add the following step to it:


you could.

Micheal Kutz, May 23, 2012 - 5:01 pm UTC

Interestingly enough, at the very very very bottom of "Chapter 78: DBMS_JOB" (Sokrate's URL) shows:

<quote>
Some legal values of what (assuming the routines exist) are:
* 'myproc(''10-JAN-82'', next_date, broken);'
* 'scott.emppackage.give_raise(''JENKINS'', 30000.00);'
* 'dbms_job.remove(job);'

So, the values JOB, NEXT_DATE, and BROKEN are documented as legal values but their purpose and usage are not documented clearly. (if they are documented at all)

I'd call that a "document bug" and assume that what Tom wrote is valid from version to version.

Tom Kyte
May 23, 2012 - 6:14 pm UTC

thank you - that does make this now "safe" :)

appreciate the followup - I got lost trying to find the anonymous block ;)


and I filed a doc bug to get the misstatement on "you MUST commit" corrected as well as get those three documented.

well hidden ...

Sokrates, May 24, 2012 - 1:24 am UTC

I learnt the job-parameter-trick by
Expert one on one Oracle
and I never used it cause I thought it was undocumented

Thanks Michael, thanks Tom !

FOUND IT!!

Micheal Kutz, May 24, 2012 - 8:35 am UTC

With the help of Support, I found it.

PACKAGE DBMS_JOB Specification (Doc ID 67686.1)
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&id=67686.1

<quote>
------------
PARAMETERS
------------

...<snip> ...
Special parameter values recognized are:
* job: an in parameter, the number of the current job
* next_date: in/out, the date of the next refresh
* broken: in/out, is the job broken. The IN values is FALSE.

MK

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library