Home > Blockchain >  Trigger to use data from 2 rows
Trigger to use data from 2 rows

Time:01-10

A bit of a background first:

I have 2 tables, HOLD and CALC. When I start a transaction, a row is created in the HOLD table with JOB_ID, STATUS:START, and TIME: SYSDATETIME. When the job is completed, another row is created in the HOLD table with the same JOB_ID, STATUS:END and TIME: SYSDATETIME. So when the transaction is completed, the HOLD table will look like this:

JOB_ID STATUS TIME
1234 START TIME1
1234 END TIME2

I plan to create a trigger that activates after the status END data is inserted. This trigger will pull the JOB_ID and calculate (TIME2 - TIME1) and insert it into the CALC table as minutes or seconds only. So in the end, there will only be 1 row for each JOB_ID.

JOB_ID TIME_TAKEN
1234 05:00

I'm currently having trouble with selecting the rows with the same JOB_ID as I keep getting error when I try to use a where clause, and how to insert the time into variables so I can use TIME_DIFF.

CREATE OR REPLACE TRIGGER "TEST".CALC_TRIGGER AFTER INSERT ON "HOLD" REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
v_STATUS VARCHAR2(1 Byte);
BEGIN
IF (:NEW.STATUS = 'END') THEN
BEGIN
    INSERT INTO CALC(JOB_ID, TIME_TAKEN)
VALUES (:OLD.JOB_ID, NULL);
END;
END IF;
END;
/

Thanks in advance!

CodePudding user response:

With sample data like here:

WITH
    tbl AS
        (
            Select 1234 "JOB_ID", 'START' "STATUS", To_Date('01.01.2023 17:12:35', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual Union All
            Select 1234 "JOB_ID", 'END' "STATUS", To_Date('04.01.2023 10:10:10', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual Union All
            
            Select 4567 "JOB_ID", 'START' "STATUS", To_Date('01.01.2023 17:12:35', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual Union All
            Select 4567 "JOB_ID", 'END' "STATUS", To_Date('03.01.2023 11:12:13', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual Union All 
            
            Select 6789 "JOB_ID", 'START' "STATUS", To_Date('01.01.2023 17:12:35', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual Union All
            Select 6789 "JOB_ID", 'END' "STATUS", To_Date('06.01.2023 17:12:35', 'dd.mm.yyyy hh24:mi:ss') "TIME" From Dual 
        ),

You can create a CTE to get your starting and ending dates(times) in the same row...

  jobs AS
    (   Select   t.JOB_ID "JOB_ID",
                (Select TIME From tbl Where JOB_ID = t.JOb_ID And STATUS = 'START') "STARTED",
                (Select TIME From tbl Where JOB_ID = t.JOb_ID And STATUS = 'END') "ENDED"
        From    tbl t
        Group By  JOB_ID
    )

... now you can get your date-time difference for any job id ...

Select  JOB_ID "JOB_ID",
        To_Char(STARTED, 'dd.mm.yyyy hh24:mi:ss' ) "STARTED",
        To_Char(ENDED,   'dd.mm.yyyy hh24:mi:ss' ) "ENDED",
        Trunc( ENDED - STARTED ) "DAYS", 
        Trunc( (ENDED - STARTED ) * 24 ) - ( Trunc( ENDED - STARTED ) * 24 ) "HOURS",
        Trunc( mod( (ENDED - STARTED ) *24 * 60, 60 ) ) "MINUTES",
        Trunc( mod( (ENDED - STARTED ) *24 *60 * 60, 60 ) ) "SECONDS"
From    jobs
Where   JOB_ID = 1234
--  
--  R e s u l t :
--      JOB_ID STARTED             ENDED                     DAYS      HOURS    MINUTES    SECONDS
--  ---------- ------------------- ------------------- ---------- ---------- ---------- ----------
--        1234 01.01.2023 17:12:35 04.01.2023 10:10:10          2         16         57         34

CodePudding user response:

As long as an access to the table is not possible in for each row trigger, you need to use a compound trigger for this:

create trigger trg_hold_
for insert on hold_
when (new.status = 'END')

compound trigger
  /*List of changed JOB_IDs*/
  g_jobs sys.odcinumberlist := sys.odcinumberlist();

after each row is
begin
  g_jobs.extend(1);
  g_jobs(g_jobs.last) := :new.job_id;
end after each row;

after statement is
begin
  /*Insert the timing into CALC table for all added job_id*/
  insert into calc(job_id, time_taken)
  select
    job_id,
    /*MAX is used to select the latest start and finish*/
    max(decode(status, 'END', time_)) - max(decode(status, 'START', time_))
  from hold_
  where job_id in (select * from table(g_jobs))
  group by job_id;
end after statement;

end;/
insert into hold_(job_id, status, time_)
values(1, 'START', systimestamp)

1 rows affected
select * from calc
insert into hold_(job_id, status, time_)
values(1, 'END', systimestamp)

1 rows affected
select * from calc
JOB_ID TIME_TAKEN
1 00 00:00:00.558130

fiddle

  • Related