I want to code a Trigger to validate if a column have only a specific value, And if some user try to update or insert a row with the same value raise the trigger. But trigger doesn't compile fine. Do you have any idea? Do you know if Compound triggers are available in 10g?
NOTE: Only one "S" value is available for mon_oficial column. If some user try to insert a new record or update with a second "S" value trigger will fired.
Table:
Trigger:
CREATE OR REPLACE TRIGGER check_mon_oficial_trg
FOR INSERT OR UPDATE
ON monedas
COMPOUND TRIGGER
v_check_moneda NUMBER(8);
BEFORE STATEMENT IS
BEGIN
SELECT COUNT(:NEW.mon_oficial)
INTO v_check_moneda
FROM monedas
WHERE mon_oficial = 'S';
IF
v_check_moneda > 1 THEN
RAISE_APPLICATION_ERROR(-20010, 'Only one official money in table');
END IF;
END BEFORE STATEMENT;
END check_mon_oficial_trg;
Error:
Compound triggers were introduced in 11gR1
Compound Triggers
A compound trigger is a Database Manipulation Language (DML) trigger that can fire at more than one timing point.
The body of a compound trigger supports a common PL/SQL state that the code for all of its sections can access. The common state is established when the triggering statement starts and destroyed when the triggering statement completes, even when the triggering statement causes an error.
Before Release 11.1, application developers modeled the common state with an ancillary package. This approach was both cumbersome to program and subject to memory leak when the triggering statement caused an error and the after-statement trigger did not fire. Compound triggers make it easier to program an approach where you want the actions you implement for the various timing points to share common data.
Related
So I am trying to use triggers to basically set some rules.. If anyone has an ID number lower than 3, he will have to pay only 100 dollars, but if someone has an ID above that, he will have to pay more. I did some research and have been told to use triggers and that triggers are very useful when fetching multiple rows. So I tried doing that but it didn't work. Basically the trigger gets created but then when i try to add values, I get the following error:-
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "S.PRICTICKET", line 6
ORA-04088: error during execution of trigger 'S.PRICTICKET'
here is what i did to make the trigger:-
CREATE OR REPLACE TRIGGER PRICTICKET BEFORE INSERT OR UPDATE OR DELETE ON PAYS FOR EACH ROW ENABLE
DECLARE
V_PRICE PAYS.PRICE%TYPE;
V_ID PAYS.ID%TYPE;
V_NAME PAYS.NAME%TYPE;
BEGIN
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS;
IF INSERTING AND V_ID<3 THEN
V_PRICE:=100;
INSERT INTO PAYS(ID,NAME,PRICE) VALUES (V_ID,V_NAME,V_PRICE);
ELSIF INSERTING AND V_ID>=3 THEN
V_PRICE:=130;
INSERT INTO PAYS(ID,NAME,PRICE) VALUES (V_ID,V_NAME,V_PRICE);
END IF;
END;
and the thing is, when i execute this code, i actually do get a message saying the trigger has been compiled. but when when i try to insert values into the table by using the following code, i get the error message I mentioned above.
INSERT INTO PAYS(ID,NAME) VALUES (19,'SS');
You're getting the error you specified, ORA-01422, because you're returning more than one row with the following SELECT:
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS;
You need to restrict the result set. For example, I'll use the :NEW psuedorecord to grab the row's new ID value, which if unique, will restrict the SELECT to one row:
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS WHERE ID = :NEW.ID;
Here is the Oracle docs on using triggers: https://docs.oracle.com/database/121/TDDDG/tdddg_triggers.htm#TDDDG99934
However, I believe your trigger has other issues, please see my comments and we can discuss.
EDIT: Based on our discussion.
ORA-04088: error during execution of trigger
Using INSERT inside a BEFORE INSERT trigger on the same table will create an infinite loop. Please consider using an AFTER INSERT and change your INSERTS to UPDATES, or an INSTEAD OF INSERT.
Additionally, remove DELETE from the trigger definition. That makes no sense in this context.
Let's begin clearing up a few things. You were told "triggers are very useful when fetching multiple rows" this is, as a general rule and without additional context, false. There are 4 types of DML triggers:
Before Statement - fires 1 time for the statement regardless of the number of rows processed.
Before Row - fires once for each row processed during the statement before old and new values are merged into a single set of values. At this point you are allowed to change the values in the columns.
After Row - fires once for row processed during the statement after merging old and new values into a single set of values. At this point you cannot change the column values.
After statement - fires once for the statement regardless of the number of rows processed.
Keep in mind that the trigger is effectively part of the statement.
A trigger can be fired for Insert, Update, or Delete. But, there is no need to fire on each. In this case as suggested, remove the Delete. But also the Update as your trigger is not doing anything with it. (NOTE: there are compound triggers, but they contain segments for each of the above).
In general a trigger cannot reference the table that it is fired upon. See error ORA-04091.
If you're firing a trigger on an Insert it cannot do an insert into that same table (also see ORA-04091) and even if you get around that the Insert would fire the trigger, creating a recursive and perhaps a never ending loop - that would happen here.
Use :New.column_name and :Old.column_name as appropriate to refer to column values. Do not attempt to select them.
Since you are attempting to determine the value of a column you must use a Before trigger.
So applying this to your trigger the result becomes:
CREATE OR REPLACE TRIGGER PRICTICKET
BEFORE INSERT ON PAYS
FOR EACH ROW ENABLE
BEGIN
if :new.id is not null
if :new.ID<3 then
:new.Price :=100;
else
:new.Price := 130;
end if ;
else
null; -- what should happen here?
end if ;
END PRICTICKET ;
I'm trying to create my trigger but it's asking for binds EVERY time. It works the way I want it to when I click apply on the window that appears... However, it will log an error...
My trigger checks to see if a client is active or not and do NOT allow changes if it is found to be active...
CREATE Trigger Client_Activity
BEFORE Insert or Update or Delete ON Client
FOR EACH ROW
DECLARE
VAR_AC char(2);
BEGIN
IF UPDATING THEN
SELECT Activity INTO VAR_AC
FROM Client_Additionals
WHERE Activity = :Old.Activity;
IF Activity = 'AC'
THEN Raise_Application_Error(-20999, 'active')
END IF;
END;
/
ORACLE VERSION 12 USING SQLDEVELOPER
You have two syntax errors in your trigger:
The IF is missing an END IF
You need to compare the content of the variable var_ac
You are missing a ; after the Raise_Application_Error()
Putting that together, you can create the trigger without problems.
However, you need to use the "Run Script" button in SQL Developer to run a PL/SQL block like that.
SQL*Plus requires no special handling:
I created a trigger like that :
create or replace
TRIGGER "TRIG_DECLENCHEMENT_PARAM"
AFTER UPDATE ON t_balise
FOR EACH ROW
WHEN (NEW.no_serie like '2%')
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
P_UPDATE_BALISE(:NEW.no_serie, :NEW.date, :NEW.vitesse);
COMMIT;
END;
P_UPDATE_BALISE it's a method in another data base which update another table. It works well like that. I want to update this Trigger and change the condition NEW.no_serie like '2%' to NEW.no_serie between 200 and 299. There is a script like Alter Trigger... which make an update of Trigger ?
The alter trigger statement only lets you "enable, disable, or compile a database trigger". The documentation states:
Note:
This statement does not change the declaration or definition of an existing trigger. To redeclare or redefine a trigger, use the CREATE TRIGGER statement with the OR REPLACE keywords.
You will have to modify your existing statement (which should probably be in source control anyway) to have the new condition:
create or replace
TRIGGER "TRIG_DECLENCHEMENT_PARAM"
AFTER UPDATE ON t_balise
FOR EACH ROW
WHEN (NEW.no_serie between 200 and 299)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
P_UPDATE_BALISE(:NEW.no_serie, :NEW.date, :NEW.vitesse);
COMMIT;
END;
You've changed that comparison from a string to a number. Hopefully no_serie is actually a number and it was the old check that was incorrectly treating it as a string, and triggering on values you didn't want (2, 20, 2000 etc.).
Not directly relevant, but having this trigger as an autonomous transaction means that if the update on t_balise is rolled back, any changes made by the call to P_UPDATE_BALISE will not be rolled back - since they've been committed independently. That isn't generally something you want - the update on this table and whatever changes are made elsewhere (you said to another table; hopefully you haven't made this autonomous because the procedure is actually updating the same table) would normally be atomic and part of the same transaction. Breaking atomicity is something that is very rarely needed or desirable, so I'd check that is really what you intended and isn't just a hack to avoid a deeper problem.
I have a trigger:
create or replace trigger trig
before insert on sistem
for each row
declare
v_orta number;
begin
SELECT v_orta INTO :new.orta_qiymet
FROM sistem;
v_orta:=(:new.riyaziyyat+:new.fizika)/2;
insert into sistem(orta_qiymet)
values(v_orta);
end trig;
When I insert a row:
insert into sistem(riyaziyyat,fizika) values(4,4)
I get an error:
Why am I getting that error?
This is fundamentally not understanding how triggers work. You can't generally select from the table the trigger is against, and a before-insert trigger shouldn't not insert into the same table again - as that would just cause the trigger to fire again, infinitely (until Oracle notices and stops it). You aren't even currently using the v_orta value you're attempting to query.
I suspect you think the trigger is instead of your original insert perhaps, and really you want to set the orta_qiymet value in the newly-inserted row automatically based on the other two columns you have supplied. To do that you don't (and can't) select those values; instead you refer to the :NEW pseudorecord as you are already doing, and then set the third column value in that same pseudorow:
create or replace trigger trig
before insert on sistem
for each row
begin
:new.orta_qiymet := (:new.riyaziyyat + :new.fizika)/2;
end trig;
/
There is a lot of information in the documentation; this is similar to one of the examples.
Few of questions for bulk-bind and trigger (Oracle 10g)
1) Will row level trigger execute in case of bulk binding ?
2) If yes then, is there any option to surpress the execution only for bulk binding ?
3) If no then, is there a way to execute row level trigger in bulk binding ?
4) Will performance hamper in case row level trigger executes for bulk binding ?
Triggers are still enabled and fired when bulk-bind inserts are performed. There is nothing intinsic you can do to stop that, but of course you can put your own logic in the trigger and the code that does the bulk insert like as follows...
In a package specification:
create or replace package my_packags is
in_bulk_mode boolean default false;
... -- rest of package spec
end;
In the trigger:
begin
if NOT my_package.in_bulk_mode then
-- do the trigger stuff
end if;
end;
In the calling code:
my_package.in_bulk_mode := true;
-- do the bulk insert
my_package.in_bulk_mode := false;
Triggers execute within the SQL engine. Bulk-binding impacts the way that the calling language (pl/sql or any OCI language) calls the SQL engine, by reducing the number of calls/statements, but should not bypass any triggers.
(Imagine you have used a trigger to add validation, logging or other constraint to a database, but a third-party application would bypass it simply through using a bulk operation - this would be a recipe for data corruption and security issues).
Your statement level trigger should fire once.
You could 'disable' your trigger by making it check an in-memory session variable before doing anything else, and explicitly setting it before a bulk operation.
Row level triggers would still fire on a per-row basis, which could have a lot more impact.