Oracle Trigger - Condition before insert - sql

I don't know what my problem when created new trigger.
Is my syntax correct? Thanks! Console Logging pane
p/s: This my console display when I try to insert values
CREATE OR REPLACE TRIGGER EX03_3
BEFORE INSERT ON HR.CHITIETDATHANG
FOR EACH ROW
DECLARE
TONGHANG NUMBER; -- Total Items
HANGHIENCO NUMBER; -- Items present
HANGDABAN NUMBER; -- Items was sales.
BEGIN
-- Get total Items
SELECT SUM(MH.SOLUONG) INTO TONGHANG
FROM HR.MATHANG MH;
-- Get total Items was sales
SELECT SUM(CTDH.SOLUONG) INTO HANGDABAN
FROM HR.CHITIETDATHANG CTDH;
-- Items present
HANGHIENCO := TONGHANG - HANGDABAN;
IF(HANGHIENCO >= HANGDABAN) THEN
HANGHIENCO := HANGHIENCO-1;
INSERT INTO HR.CHITIETDATHANG VALUES(:NEW.SOHOADON,:NEW.MAHANG,
:NEW.GIABAN,:NEW.SOLUONG,:NEW.MUCGIAMGIA);
ROLLBACK;
END IF;
NULL;
END;

Seem two critical mistakes
1) trigger tries to insert into HR.CHITIETDATHANG in the body of insert trigger of HR.CHITIETDATHANG.
2) to use rollback after an insert statement is useless.
Note : I can see nothing relevant to make raise no_data_found in those select statements. EX03_3 and EX04_4 are confused, as Kaushik Nayak says.

Related

Updating the record of same table when new record is inserted or updated in oracle

I am new to learning Oracle. I have a task in which I need to update value of any previous record if new record contains its reference.
Table structure is as below :
Review_Table
(review_id number pk,
review_name varchar2,
previous_review number null,
followup_review number null
)
Here previous_review and followup_review columns are objects of same table i.e Review_table.
Now consider we have two records in Review_table A and B, A does not have any previous or followup review. When user creates/updates the record B and he selects record A as previous record, then we want to automatically update (via trigger) the value of A record's followup review with B's Review ID.
I have tried writing following trigger
create or replace trigger "REVIEW_T1"
AFTER insert or update on "REVIEW_TABLE"
for each row
begin
update REVIEW_TABLE
set review_follow_up_review = :new.REVIEW_ID
where REVIEW_ID = :new.REVIEW_PREVIOUS_REVIEW;
end;
But I am getting error as : REVIEW_TABLE is mutating, trigger/function may not see it ORA-06512
I have tried searching everything but was unable to find any solution for it
TL;DR: No trigger, no mutating. Do not use trigger to change another row in the same table.
I absolutely agree with #StevenFeuerstein's comment:
I also suggest not using a trigger at all. Instead, create a package that contains two procedures, one to insert into table, one to update. And within these procedures, implement the above logic. Then make sure that the only way developers and apps can modify the table is through this package (don't grant privs on the table, only execute on the package).
Take a look at the following example.
Prepare the schema:
create table reviews (
id number primary key,
name varchar2 (32),
previous number,
followup number
);
create or replace procedure createNextReview (name varchar2, lastId number := null) is
lastReview reviews%rowtype;
nextReview reviews%rowtype;
function getLastReview (lastId number) return reviews%rowtype is
begin
for ret in (
select * from reviews where id = lastId
for update
) loop return ret; end loop;
raise_application_error (-20000, 'last review does not exist');
end;
procedure insertReview (nextReview reviews%rowtype) is
begin
insert into reviews values nextReview;
exception when others then
raise_application_error (-20000, 'cannot insert next review');
end;
procedure setFollowUp (nextId number, lastId number) is
begin
update reviews set
followup = nextId
where id = lastId
;
exception when others then
raise_application_error (-20000, 'cannot update last review');
end;
begin
if lastId is not null then
lastReview := getLastReview (lastId);
end if;
nextReview.id := coalesce (lastReview.id, 0)+1;
nextReview.name := name;
nextReview.previous := lastId;
insertReview (nextReview);
if lastReview.Id is not null then
setFollowUp (nextReview.id, lastReview.Id);
end if;
exception when others then
dbms_output.put_line (
'createNextReview: '||sqlerrm||chr(10)||dbms_utility.format_error_backtrace ()
);
end;
/
Execute:
exec createNextReview ('first review')
exec createNextReview ('next review', 1)
See the outcome of work done:
select * from reviews;
ID NAME PREVIOUS FOLLOWUP
---------- ---------------- ---------- ----------
1 first review 2
2 next review 1
First you need to read about triggers, mutating table error and compound triggers: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS2005
Your trigger is AFTER UPDATE OR INSERT. Means if you run UPDATE OR INSERT statements on this table, the trigger will fire. But you are trying to update the same table again inside your trigger, which is compl. wrong.
I think you can fix this by rewriting this as a before trigger, rather than an after trigger.

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;

Oracle Sql trigger before update of column

I want to create an update trigger for a table called purchases that will update the total cost field and also log a record in the purchases log table to indicate which purchase was updated.
I don't have much experience with creating triggers but so far this is what i've got
CREATE OR REPLACE TRIGGER Update_Purchase_Audit BEFORE
UPDATE ON Purchases
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
WHEN(NEW.p_id>0)
DECLARE total_cost INTEGER;
BEGIN
set total_cost := (:NEW.quantity * :NEW.unitcost);
:NEW.total_cost:=total_cost*0.165;
INSERT INTO Purchase_Log(eventdate, p_id, descrip)
VALUES(SYSDATE, :NEW.p_id, :NEW.product);
END;
/
I get this error when creating the trigger:
trigger created with compilation errors. When I run show errors; I get ORA-00922: missing or invalid option
Any suggestion? Thanks
CREATE OR REPLACE TRIGGER Update_Purchase_Audit
BEFORE UPDATE ON Purchases
FOR EACH ROW
WHEN (NEW.p_id > 0)
DECLARE
total_cost number;
BEGIN
total_cost := :NEW.quantity * :NEW.unitcost;
:NEW.total_cost := total_cost * 0.165; --<< this is ok BEFORE update
-->> Do you have table relations here? THis may need to go AFTER update
-- in separate trigger
INSERT INTO Purchase_Log (eventdate, p_id, descrip)
VALUES(SYSDATE, :NEW.p_id, :NEW.product);
END;
/

Count(*) not working properly

I create the trigger A1 so that an article with a certain type, that is 'Bert' cannot be added more than once and it can have only 1 in the stock.
However, although i create the trigger, i can still add an article with the type 'Bert'. Somehow, the count returns '0' but when i run the same sql statement, it returns the correct number. It also starts counting properly if I drop the trigger and re-add it. Any ideas what might be going wrong?
TRIGGER A1 BEFORE INSERT ON mytable
FOR EACH ROW
DECLARE
l_count NUMBER;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT COUNT(*) INTO l_count FROM mytable WHERE article = :new.article;
dbms_output.put_line('Count: ' || l_count);
IF l_count >0 THEN
IF(:new.TYPEB = 'Bert') THEN
dbms_output.put_line('article already exists!');
ROLLBACK;
END IF;
ELSIF (:new.TYPEB = 'Bert' AND :new.stock_count>1) THEN
dbms_output.put_line('stock cannot have more than 1 of this article with type Bert');
ROLLBACK;
END IF;
END;
This is the insert statement I use:
INSERT INTO mytable VALUES('Chip',1,9,1,'Bert');
A couple of points. First, you are misusing the autonomous transaction pragma. It is meant for separate transactions you need to commit or rollback independently of the main transaction. You are using it to rollback the main transaction -- and you never commit if there is no error.
And those "unforeseen consequences" someone mentioned? One of them is that your count always returns 0. So remove the pragma both because it is being misused and so the count will return a proper value.
Another thing is don't have commits or rollbacks within triggers. Raise an error and let the controlling code do what it needs to do. I know the rollbacks were because of the pragma. Just don't forget to remove them when you remove the pragma.
The following trigger works for me:
CREATE OR REPLACE TRIGGER trg_mytable_biu
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
WHEN (NEW.TYPEB = 'Bert') -- Don't even execute unless this is Bert
DECLARE
L_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO L_COUNT
FROM MYTABLE
WHERE ARTICLE = :NEW.ARTICLE
AND TYPEB = :NEW.TYPEB;
IF L_COUNT > 0 THEN
RAISE_APPLICATION_ERROR( -20001, 'Bert already exists!' );
ELSIF :NEW.STOCK_COUNT > 1 THEN
RAISE_APPLICATION_ERROR( -20001, 'Can''t insert more than one Bert!' );
END IF;
END;
However, it's not a good idea for a trigger on a table to separately access that table. Usually the system won't even allow it -- this trigger won't execute at all if changed to "after". If it is allowed to execute, one can never be sure of the results obtained -- as you already found out. Actually, I'm a little surprised the trigger above works. I would feel uneasy using it in a real database.
The best option when a trigger must access the target table is to hide the table behind a view and write an "instead of" trigger on the view. That trigger can access the table all it wants.
You need to do an AFTER trigger, not a BEFORE trigger. Doing a count(*) "BEFORE" the insert occurs results in zero rows because the data hasn't been inserted yet.

Creating trigger which throws an exception on insert

Hello fellow programmers and happy new year to you all!
I have few university tasks for winter break and one of them is to create trigger on table:
PERSON(ID, Name, Surname, Age);
Trigger is supposed to inform user when they have inserted row with invalid ID. Vadility criteria is that ID is 11 digits long.
I tried to write solution like this:
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
DECLARE
idNew VARCHAR(50);
lengthException EXCEPTION;
BEGIN
SELECT id INTO idNew FROM INSERTED;
IF LENGTH(idNew) <> 11 THEN
RAISE lengthException;
END IF;
EXCEPTION
WHEN lengthException THEN
dbms_output.put_line('ID for new person is INVALID. It must be 11 digits long!');
END;
Then I realized that INSERTED exists only in sqlserver and not in oracle.
What would you suggest I could do to fix that?
Thanks in advance!
Do you want to raise an exception (which would prevent the insert from succeeding)? Or do you want to allow the insert to succeed and write a string to the dbms_output buffer that may or may not exist and may or may not be shown to a human running the insert?
In either case, you'll want this to be a row-level trigger, not a statement-level trigger, so you'll need to add the for each row clause.
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
FOR EACH ROW
If you want to raise an exception
BEGIN
IF( length( :new.id ) <> 11 )
THEN
RAISE_APPLICATION_ERROR( -20001,
'The new ID value must have a length of 11' );
END IF;
END;
If you want to potentially print output but allow the insert to succeed
BEGIN
IF( length( :new.id ) <> 11 )
THEN
dbms_output.put_line( 'The new ID value must have a length of 11' );
END IF;
END;
Of course, in reality, you would never use a trigger for this sort of thing. In the real world, you would use a constraint.