APEX - 21.1 - Calculate the ageing of a task - sql

I have columns Status, Updated_Time, Ageing
When status is marked as WIP, I have below triggger to update the "Updated_Time" column with PST systime
create or replace TRIGGER TRIG01
BEFORE INSERT OR UPDATE ON TASKS
REFERENCING FOR EACH ROW
WHEN (NEW.STATUS='WIP')
BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
END IF;
END;
I'm planning to insert one more line to the trigger to calculate the ageing. It seems to be working via SQL command but from the interactive grid. Any suggestions on how to calculate the ageing of a task.
:NEW.AGEING := ROUND((CAST(NEW_TIME(SYSDATE, 'GMT', 'PDT') as DATE)- CAST(:NEW.UPDATED_DATE as DATE))*1440,2);

In Oracle or relational database in general, insert/update(DMLs) needed to be completed as part of single transaction and requires you to perform commit or rollback.
My guess here(as you have not shared specific details about how IG is programmed) is that it does not get committed and timed out.
My suggestion is to get either of this to solve the problem :
Use pragma within your trigger Or
Use Apex dynamic action to calculate Date and Aging

Related

Oracle trigger error ORA-04091

I get an error (ORA-04091: table DBPROJEKT_AKTIENDEPOT.AKTIE is mutating, trigger/function may not see it) when executing my trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
AFTER
INSERT OR UPDATE OF TAGESKURS
OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
bfr number;
Begin
bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
UPDATE AKTIE
SET BILANZ = TAGESKURS - WERT_BEIM_EINKAUF;
IF bfr < -50
THEN
DBMS_OUTPUT.PUT_LINE('ACHTUNG: The value (Nr: '||:new.AKTIEN_NR||') is very low!');
END IF;
END;
I want to check the value "BILANZ" after calculating it, wether it is under -50.
Do you have any idea why this error is thrown?
Thanks for any help!
There are several issues here:
Oracle does not allow you to perform a SELECT/INSERT/UPDATE/DELETE against a table within a row trigger defined on that table or any code called from such a trigger, which is why an error occurred at run time. There are ways to work around this - for example, you can read my answers to this question and this question - but in general you will have to avoid accessing the table on which a row trigger is defined from within the trigger.
The calculation which is being performed in this trigger is what is referred to as business logic and should not be performed in a trigger. Putting logic such as this in a trigger, no matter how convenient it may seem to be, will end up being very confusing to anyone who has to maintain this code because the value of BILANZ is changed where someone who is reading the application code's INSERT or UPDATE statement can't see it. This calculation should be performed in the INSERT or UPDATE statement, not in a trigger. It considered good practice to define a procedure to perform INSERT/UPDATE/DELETE operations on a table so that all such calculations can be captured in one place, instead of being spread out throughout your code base.
Within a BEFORE ROW trigger you can modify the values of the fields in the :NEW row variable to change values before they're written to the database. There are times that this is acceptable, such as when setting columns which track when and by whom a row was last changed, but in general it's considered a bad idea.
Best of luck.
You are modifying the table with the trigger. Use a before update trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
BEFORE INSERT OR UPDATE OF TAGESKURS OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
v_bfr number;
BEGIN
v_bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
:new.BILANZ := v_bfr;
IF v_bfr < -50 THEN
Raise_Application_Error(-20456,'ACHTUNG: The value (Nr: '|| :new.AKTIEN_NR || ') is very low!');
END IF;
END;

Create Instead of Insert Trigger

I am a beginner at Oracle. I am trying to create an INSTEAD OF trigger to enforce a rule that no tutor should work more than 60 hours in a month
This is what I have so far
CREATE TRIGGER limit_hour
INSTEAD OF INSERT ON SESSIONHOURS
DECLARE
totalHours NUMBER := 60;
monthOnly DATE;
totalSession NUMBER;
FOR EACH ROW
BEGIN
INSERT INTO SESSIONHOURS(SESSIONDATEKEY, TUTORKEY, TOTALSESSION)
SELECT EXTRACT (MONTH FROM DATE S.SESSIONDATEKEY), S.TOTALSESSION
INTO monthOnly, totalSession
FROM SESSIONHOURS S
END;
The error "inappropriate INTO" keeps popping up. Also I need to assign the total sum of each session(30 min each) for the extracted month and then compare it with the "totalHour". How do I assign a time value to a date value? Any suggestions would be appreciated
Rather than use an INSTEAD OF trigger, it seems to me that a BEFORE INSERT trigger would be more appropriate. INSTEAD OF triggers are commonly used to map INSERTs on non-insertable views into INSERTs into the desired tables. A BEFORE INSERT trigger, on the other hand, is fired before each row is inserted into the table, allowing the values in the row to be checked for consistency, etc. Such a trigger might be used as follows:
CREATE TRIGGER SESSIONHOURS_BI
BEFORE INSERT INTO SESSIONHOURS
FOR EACH ROW
DECLARE
nTotal_tutor_hours NUMBER;
BEGIN
SELECT SUM(HOURS)
INTO nTotal_tutor_hours
FROM SESSIONHOURS s
WHERE s.TUTORKEY = :new.TUTORKEY;
IF nTotal_tutor_hours + :new.HOURS > 60 THEN
RAISE_APPLICATION_ERROR(-20001, 'Addition of ' || :new.HOURS ||
' for tutor ' || :new.TUTORKEY ||
' exceeds monthly limit of 60');
END IF;
END SESSIONHOURS_BI;
This trigger will be fired before an INSERT into SESSIONHOURS is processed. It queries the database to determine the total number of hours worked by the tutor whose key is in the INSERT statement (:new.TUTORKEY). If the total hours worked PLUS the hours in the new record exceeds 60 an exception is raised, which causes the INSERT to be aborted. Otherwise the trigger returns normally and the INSERT proceeds.
HOWEVER - even this won't work. The problem is that the trigger is defined on the table SESSIONHOURS, and inside the trigger there is a SELECT on the SESSIONHOURS table. This will cause the database to throw the dreaded ORA-04091 exception, with explanatory text table SESSIONHOURS is mutating, trigger/function may not see it. There are several ways to fix this, the BEST of which is to follow a simple rule:
***NEVER* IMPLEMENT BUSINESS LOGIC IN A TRIGGER!!!!
A rule such as "tutors may not work more than 60 hours" is a business rule. This should be implemented in your application logic, not in a trigger. Create a procedure or function in the database to perform the INSERT INTO SESSIONHOURS and any needed validation logic, and call that procedure every time you need to insert data into SESSIONHOURS. Don't try putting the validation logic into a trigger - you'll find it's rather difficult, and will lead to never-ending debugging sessions, as noted here.
Best of luck.
Your INSERT statement is improperly written. It should be:
INSERT INTO SESSIONHOURS(SESSIONDATEKEY, TOTALSESSION)
SELECT EXTRACT (MONTH FROM DATE S.SESSIONDATEKEY), S.TOTALSESSION
FROM SESSIONHOURS S
This won't solve your "total hours" issues, but it takes care of the error you reported.
Best of luck.

Using Sysdate in trigger

Hello guys I'm a bit of a noob when it comes to triggers so I'm just looking for some advice on how to do the follow trigger.
I have created a trigger that will throw an error message if someone was to delete a record during office hours however I would like to create another trigger that uses SYSDATE will not delete records from say today and future dates.
I was thinking of maybe using >=SYSDATE but I'm not sure if that is a valid sql statement.
CREATE OR REPLACE TRIGGER records_delete
BEFORE DELETE
ON RECORDS FOR EACH ROW
BEGIN
IF TO_CHAR(SYSDATE, 'HH24MI') NOT >= sysdat
RAISE_APPLICATION_ERROR(-20669, 'You can not delete current or future records');
END IF;
END records_delete;
Thanks, leprejohn
The problem here is that you're not referencing any of the fields of the table that you're deleting from.
If your table had a column called record_date then you could rewrite this as:
CREATE OR REPLACE TRIGGER records_delete
BEFORE DELETE
ON RECORDS FOR EACH ROW
BEGIN
IF (:old.record_date >= SYSDATE) THEN
RAISE_APPLICATION_ERROR(-20669, 'You can not delete current or future records');
END IF;
END records_delete;
The syntax :old. and :new. are how you reference the columns of the current record being acted upon by the trigger, with :old. being the prefix for inspecting the values before the trigger acts on it and :new. being for values after the trigger is done. This syntax allows you to see the values before/after the trigger updates the data, which in your case doesn't matter since you're deleting records.
If you want to disregard the hours, mins, seconds of the date fields, use this in the IF statement
trunc(:old.record_date) >= trunc(SYSDATE)
If record_date is actually stored as a string instead of a date, you would convert it to a date before comparison:
to_date(:old.record_date, 'DDMMYYYY') >= trunc(SYSDATE)
Update the format mask 'DDMMYYYY" to the format your date is actually stored in. Check the Oracle documentation for description of to_date and date format models.
Give this a shot and see if it works.

PL/SQL table level trigger running after second update

I have this problem whith this trigger which is calling a procedure updating a table after updating a row in other table.The problem is that you have to update table STAVKARACUNA two times to table RACUN update but it uses old values. Here is a code of both:
Here is a code of aa procedure:
create or replace PROCEDURE ukupnaCenaRacun (SIF IN VARCHAR2) AS
SUMA float := 0;
suma2 float := 0;
Mesec NUMBER;
popust float :=0.1;
BEGIN
SELECT SUM(iznos) INTO SUMA
FROM STAVKARACUNA
WHERE SIF = SIFRARAC;
SELECT SUM(vredrobe*pdv) INTO SUMA2
FROM STAVKARACUNA
WHERE SIF = SIFRARAC;
SELECT EXTRACT (MONTH FROM DATUM) INTO Mesec FROM RACUN WHERE SIF=SIFRARAC;
IF(Mesec = 1) THEN
UPDATE RACUN
SET PDVIZNOS = SUMA2, ukupnozanaplatu = suma*(1-popust)
WHERE SIFRARAC=SIF;
END IF;
IF (MESEC != 1) THEN
UPDATE RACUN
SET PDVIZNOS = SUMA2, ukupnozanaplatu = suma
WHERE SIFRARAC=SIF;
END IF;
END;
Here is a trigger:
create or replace TRIGGER "UKUPNACENA_RACUN_UKUPNO"
AFTER INSERT OR UPDATE OR DELETE OF CENA,KOL,PDV ON STAVKARACUNA
DECLARE
SIF VARCHAR2(20) := PACKAGE_STAVKARACUNA.SIFRARAC;
BEGIN
PACKAGE_STAVKARACUNA.ISKLJUCI_TRIGER('FORBID_UPDATING');
ukupnaCenaRacun(SIF);
PACKAGE_STAVKARACUNA.UKLJUCI_TRIGER('FORBID_UPDATING');
END;
The problem is when a table STAVKARACUNA is updated, nothing happens with table RACUN, but next time table STAVKARACUNA is updated the data in table RACUN is updated but with old values.
Thank you very much.
Are you aware that a trigger for an event on a table should not directly access that table? The code is inside a DML event. The table is right in the middle of being altered in some say. So any query back to the same table could well attempt to read data that is in the process of being changed. It could try to read data that does not quite exist before a commit is performed or is one value now but will be a different value once a commit is performed. The table is mutating.
This goes for any code outside the trigger that the triggers calls. So the ukupnaCenaRacun procedure is executed in the context of the trigger. Yet it goes out and queries table STAVKARACUNA in two places (which can be placed in a single query but that is neither here nor there).
Since you're not getting a mutating table error, I can only assume that the update is not taking place until after the triggering event is committed but then you won't see the results until after that is committed sometime later -- like when the second update is committed.
That explanation actually sounds hollow to me as I have always thought that all activity performed by a trigger is committed or rolled back as part of one transaction. But that is the action you are describing.
It appears that SIF is a package variable defined in the package spec. Since everything in the procedure keys off that value and the trigger doesn't change the value, can't SUMA and SUMA2 also be defined as variables, values to be updated whenever SIF changes?

SQL Oracle Trigger to change date

CREATE OR REPLACE TRIGGER shares_to_amount
AFTER INSERT OR UPDATE OF issued ON shares_amount
FOR EACH ROW
BEGIN
INSERT INTO shares_amount(
share_issue_id,
share_id,
issued,
date_start,
date_end
) VALUES (
:OLD.share_issue_id,
:OLD.share_id,
:NEW.issued,
:NEW.date_start,
:((NEW.date_start)-1).date_end
);
END;
I want to change the date_end to the date_new date -1 when a new share value is issued into 'issued'. The start date can be today's date but the end date will have to display the day before.
Fist of all your trigger can't work because of mutating table problem. You can't execute DMLs or queries in row-level triggers agains the table which is being changed by triggering DML (if the trigger is not autonomous but this is dangeros and exceptionac case). If I understand your question right you want to keep the history of changes made for shares. Th best way is to create PL/SQL package, incapsulate logic into procedures and provide this interface to end-users or other programms.