Oracle SQL Instead of Delete Trigger - sql

I want to create an 'instead of delete' trigger to restrict the deletion of a row when the value entered exceeds a certain amount.
I have an Invoice table with 2 columns: Invoice(InvoiceID Number, Total Number)
I want the trigger to fire if I try to delete a row that has a stored value in Total >= 100 and prevent the deletion.
So far I have a rough sketch of what I want, but I'm not sure if the exact syntax is correct.
CREATE OR REPLACE TRIGGER IOFD_INVOICE
INSTEAD OF DELETE ON INVOICE
BEGIN
DECLARE
TTL INTEGER;
SELECT TOTAL = TTL
FROM INVOICE
IF TTL >= 100
BEGIN
RAISERROR('Record cannot be deleted.')
ROLLBACK
END
ELSE
BEGIN
DELETE FROM INVOICE
END
END;
I thought instead of triggers could be used on tables but I get the following error Message:
Error report -
ORA-25002: cannot create INSTEAD OF triggers on tables
25002. 00000 - "cannot create INSTEAD OF triggers on tables"
*Cause: Only BEFORE or AFTER triggers can be created on a table.
*Action: Change the trigger type to BEFORE or AFTER.

INSTEAD OF triggers are only applicable to views.
The logic can be accomplished by a simple
CREATE OR REPLACE TRIGGER BD_INVOICE
BEFORE DELETE ON INVOICE FOR EACH ROW
BEGIN
IF :OLD.TOTAL >= 100 THEN
RAISE_APPLICATION_ERROR(-20001,'Record cannot be deleted.');
END IF;
END;

Related

Failing to produce or update a table row using a trigger in SQL

I am trying to create a trigger in Oracle SQL 12c.
The concept is that i have two tables, a storage table (Storage) that holds items and a delivery table (Supplier_Orders) that notes how many items(Units) are being delivered to the storage. The trigger is supposed to detect whether an item that is being delivered to the storage (new_product_id) already exists in the storage(product_id), and if not it automatically adds it and also asks the user to input some mandatory values(barcode and sell_price). If it does exist, it updates the table by adding the items being delivered (Units) to the total of items(remaining_units).
create or replace trigger SL_ORD_ADDER_TR
before insert on Supplier_Orders
for each row
begin
if :new.new_product_id in (select s.product_id from storage s where (:new.new_product_id=s.product_id)) then
update storage
set remaining_units = remaining_units + new.units
where new.new_product_id=product_id;
end if;
if :new.new_product_id not in(select s.product_id from storage s where (:new.new_product_id=s.product_id)) then
insert into storage(product_id,product_name,remaining_units,barcode,sell_price_per_unit) values(new.new_product_id,new.product_name,new.units,'&barcode','&sell price');
end if;
end;
The errors produced are the following:
Error(5,5): PL/SQL: Statement ignored
Error(5,31): PLS-00405: subquery not allowed in this context
Error(10,5): PL/SQL: Statement ignored
Error(10,34): PLS-00405: subquery not allowed in this context
The Subquery is not allowed at PL/SQL, after IF condition in your code. It's allowed only in SQL.
You can use a variable to check the "product_id" in the "storage" table and determine the IF condition on it.
CREATE OR REPLACE TRIGGER SL_ORD_ADDER_TR
BEFORE INSERT on Supplier_Orders
REFERENCING
NEW AS new
OLD AS old
for each row
DECLARE
-- New variable
v_cnt number;
begin
select count(s.product_id) into v_cnt from storage s where s.product_id=:new.new_product_id;
if v_cnt>0
-- It exists, so updating
then
update storage
set remaining_units = remaining_units + :new.units
where product_id=:new.new_product_id;
end if;
if v_cnt=0
-- It not exists, so inserting
then
insert into storage(product_id,product_name,remaining_units,barcode,sell_price_per_unit)
values(:new.new_product_id,:new.product_name,:new.units,'&barcode','&sell price');
end if;
end;

PL/SQL ORACLE: Trigger updates on delete

I am trying to make a trigger that increases the booked value and decreases the available value whenever new record is inserted inside the table ticket_price. If a record is deleted, I want it to decrease the booked value and increase the available value.
Although I am able to successfully make the trigger work for INSERT, I am unable to do the same for updating the values on deletion of a record.T his is the error I get whenever I try to delete a record
ORA-01403: no data found
ORA-06512: at "K.CAL", line 6
ORA-04088: error during execution of trigger 'K.CAL'
Just to clarify, I am updating values in another table, not the same table I am deleting!
Here is my code for the trigger:
CREATE OR REPLACE TRIGGER cal
BEFORE INSERT OR DELETE ON TICKET_PRICE FOR EACH ROW
DECLARE
V_TICKET TICKET_PRICE.TICKETPRICE%TYPE;
V_BOOKED FLIGHTSEAT.BOOKED_SEATS%TYPE;
V_AVAILABLE FLIGHTSEAT.AVAILABLE_SEATS%TYPE;
BEGIN
SELECT BOOKED_SEATS,AVAILABLE_SEATS
INTO V_BOOKED,V_AVAILABLE
FROM FLIGHTSEAT
WHERE SEAT_ID=:NEW.SEAT_ID;
IF INSERTING THEN
V_BOOKED:=V_BOOKED+1;
V_AVAILABLE:=V_AVAILABLE-1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=:NEW.SEAT_ID;
ELSIF DELETING THEN
V_BOOKED:=V_BOOKED-1;
V_AVAILABLE:=V_AVAILABLE+1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=1;
END IF;
END;
You have correctly surmised that :new.seat is not available on the update for a delete. But neither is it available for the select and ow could you know sea_id=1 was need to be updated? For reference to Deleted row data use :Old.column name; is this case use :old_seat_id for both select and update.
But you don't need the select at all. Note: Further you have an implied assumption that seat_id is unique. I'll accept that below.
create or replace trigger cal
before insert or delete on ticket_price
for each row
declare
v_seat_id flightseat.seat_id%type;
v_booked flightseat.booked_seats%type;
begin
if INSERTING then
v_booked := 1;
v_seat_id := :new.seat_id;
else
v_booked := -1;
v_seat_id := :old.seat_id;
end if;
update flightseat
set booked_seats=booked_seats+v_booked
, available_seats=available_seats-v_booked
where seat_id = v_seat_id;
end;

pl/sql trigger error : exact fetch returns more than requested number of rows

I am having a tough time understanding what is wrong with my pl/sql trigger.
The error is :
Error report -
SQL Error: ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "SYSTEM.TRG_LATE_RETURN", line 6
ORA-04088: error during execution of trigger 'SYSTEM.TRG_LATE_RETURN'
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
My trigger is as follows:
create or replace trigger trg_late_return
before update of DETAIL_RETURNDATE, DETAIL_DUEDATE on TY_DETAILRENTAL
declare
temp_date DATE:= SYSDATE;
temp_due_date DATE:= SYSDATE;
BEGIN
select DETAIL_RETURNDATE
into temp_date
from TY_DETAILRENTAL;
select DETAIL_DUEDATE
into temp_due_date
from TY_DETAILRENTAL;
IF temp_date is null
THEN
update TY_DETAILRENTAL
set DETAIL_DAYSLATE=null;
END IF;
if temp_date <> null
THEN
if temp_date = trunc(temp_due_date) + 1
then
update TY_DETAILRENTAL
set DETAIL_DAYSLATE=0;
end if;
if temp_date > trunc(temp_due_date) + 1
then
update TY_DETAILRENTAL
set DETAIL_DAYSLATE = DETAIL_RETURNDATE - DETAIL_DUEDATE;
end if;
end if;
END;
/
New to SQL and PL/SQL so I would appreciate any help.
None of your queries or updates have filters (where clauses) so you are working on the entire table - that can't be what you intended. You are trying to get all of the value of those columns into scalar variables, which can only hold a single value.
The trigger is also against the same table you're querying and updating, which suggests you actually meant this to be (or need this to be) a row level trigger, not a statement level trigger.
That means you need to add for each row to the definition, and instead of requerying and updating the table, you can operate on the new pseudorecord that is available for row level triggers.
You also can't use <> to compare with null; you're already using is null, and the opposite of that is is not null. Although you could also just use else here.
So this might be what you wanted:
create or replace trigger trg_late_return
before update of detail_returndate, detail_duedate
on ty_detailrental
for each row
begin
if :new.detail_returndate is null
then
:new.detail_dayslate := null;
else
if :new.detail_returndate = trunc(:new.detail_duedate) + 1
then
:new.detail_dayslate := 0;
elsif :new.detail_returndate > trunc(:new.detail_duedate) + 1
then
:new.detail_dayslate := :new.detail_returndate - :new.detail_duedate;
end if;
end if;
end;
/
You can refer to the updated columns value with :new when comparing them too, so you don't need the local variable copies of those values. (You can look at the pre-update values with :old too, but that doesn't seem to be needed here).
Changes you make to the new pseudorecord are used when the actual matching row in the database is finally updated.
Read more about the new/old pseudorecords, and about triggers in general.

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 Triggers calling upon multiple tables

I'm creating a row-level trigger for Oracle using SQL Tools. I will attach a picture of the ERD I am working off of. I will also include my instructions, paste what code I have tried, and the error I am getting.
Description of request:
Create a row-level trigger named "TRG_USED_COST" on the USED table. The trigger should execute BEFORE a new row is inserted in the table. The insert command will provide a value for "CHEM_NUM","JOB_NUM", and "USED_QTY" that are being added to the table. Using the CHEM_NUM being used in the insert, retrieve the "CHEM_UNIT_COST" from the CHEMICAL table. Multiply the chemical unit cost times the quantity of chemicals used in this row, and include that value as the "USED_CHARGE" for the row being inserted.
My code:
CREATE OR REPLACE TRIGGER TRG_USED_COST
BEFORE INSERT OR UPDATE OF CHEM_NUM,JOB_NUM,USED_QTY ON USED
FOR EACH ROW
BEGIN
UPDATE USED
SET USED_CHARGE = CHEM_UNIT_COST * USED_QTY
WHERE CHEM_NUM IS NOT NULL;
END;
/
After I enter the code, I receive the error:
PL/SQL: ORA-00904: "CHEM_UNIT_COST": invalid identifier
I know that I'm getting the error because I did not reference the CHEMICAL table that "CHEM_UNIT_COST" is located in...but I don't know how to reference it.
You have several issues here. First, you do not "update" the target table in this case. Use the :new attribute on the trigger. Then select the cost from the other table.
CREATE OR REPLACE TRIGGER TRG_USED_COST
BEFORE INSERT OR UPDATE OF CHEM_NUM,JOB_NUM,USED_QTY ON USED
FOR EACH ROW
DECLARE
v_cost CHEMICAL.CHEM_UNIT_COST%TYPE;
BEGIN
SELECT CHEM_UNIT_COST
INTO v_cost
FROM CHEMICAL
WHERE CHEM_NUM = :new.CHEM_NUM;
:new.USED_CHARGE := v_cost * :new.USED_QTY;
END;
/