I have question about triggers. For my database assignment, I have to create a trigger that automatically generates an ID number, which I have done.
The problem is a second trigger needs to be made that also acts on the same data. Both are inserts. Since they have to be separate, I am not sure how to make this work.
From what I have been taught the way to make a trigger act on the most recent addition is to use:
WHERE ID=:NEW.ID;
where ID is the primary key, but this does not work for me when the ID is being generated by a trigger. Is there a method of creating a trigger that acts on the most recent row added to the table that does not reference the primary key?
It is not clear why you need two triggers but let's assume it really makes sense (which I doubt).
Does the first trigger generates the ID from a sequence? In this case you can use value CURRVAL of the sequence. This pseudocolumn returns the current value of a sequence without increasing the value, see Sequence Pseudocolumns
If this does not fit your needs you can write a procedure which is then called be the trigger.
Would be like this:
create procedure PROC(aRow in ROWID) as
begin
...
end;
create first_trigger ....
begin
... whatever is needed at first trigger.
PROC(NEW.ROWID);
end;
create second_trigger ....
begin
... whatever is needed at second trigger.
PROC(NEW.ROWID);
end;
Both triggers would operate on the same row. You can also write current row values into a PL/SQL variable and process them by a Statement-Trigger (i.e. no row-level-trigger)
Related
I'm having a little trouble with understanding functions and triggers in sql. I didn't post the code of procedure chkInsertAritcle but let's say it returns NEW if it managed to make change and NULL if it didn't.
So my question is about the trigger. If I put AFTER INSERT does that means that it will complete INSERT without depending on the return value? And what
happens with the rest of the rows?
Next question is if I put BEFORE INSERT, in what order does code runs?
Thanks!
CREATE TRIGGER ArticleIns
AFTER INSERT ON ListOfArticles
FOR EACH ROW
EXECUTE PROCEDURE chkInsertArticle();
First all BEFORE triggers run in alphabetical order, then the operation is performed, then all AFTER triggers run in alphabetical order.
Each operation sees the result of the previous one as input, and if any trigger returns NULL, processing for that row stops. So if a BEFORE trigger returns NULL, the DML operation won't take place.
This happens independently for each row affected by the triggering DML statement.
So if the trigger runs before insert, then the code runs before the data is inserted into the row and constraints are checked. So for example you might want to add a timestamp before the data is committed to the database,
If it runs after then the data is already present in the table and all constraints have been checked. This is usually where you want to trigger another process based on the row data, maybe update another table, send an e-mail etc.
In your example, the data will be in the database before your procedure runs. So if your procedure modifies the row data, it needs to be in the database.
The problem here is that, we use an application called ArcGIS that creates an upper management layer for our database, and when our users use ArcGIS to create a new object in the database, it adds a default value (0) to the primary key, so when the second object is created it triggers an ORA error for having duplicate values.
So my idea was to create an auto increment trigger for our PK but use AFTER INSERT instead.
I couldn't find any example online for this specific case and simply switching BEFORE to AFTER gives an error saying that you can't use NEW with AFTER
SQL code of what I tried (taken from other questions):
CREATE OR REPLACE TRIGGER "IMOVEL_TRIGGER"
after insert on IMOVEL
for each row
begin
select IMOVEL_SEQ.nextval into :NEW.GEOCODIGO_IMOVEL from dual;
end;
It cant be a BEFORE INSERT trigger, because the application overwrites it
Simplifying, what I need is a AFTER INSERT trigger, that updates the PK to the sequence .nextval, it doesn't let me use :OLD or :NEW, so I'm not sure what must be done.
Or an update trigger that only runs after it is created
This is pretty new territory for me, having to learn SQL now just to solve this issue
You can change :NEW values only in a BEFORE trigger. By the time you reach the AFTER trigger, the row has already been inserted, so it's too late to change the columns.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7004.htm says:
Restrictions on BEFORE Triggers
BEFORE triggers are subject to the following restrictions:
You cannot specify a BEFORE trigger on a view or an object view.
You can write to the :NEW value but not to the :OLD value.
Restrictions on AFTER Triggers
AFTER triggers are subject to the following restrictions:
You cannot specify an AFTER trigger on a view or an object view.
You cannot write either the :OLD or the :NEW value.
It's not clear why you want to use an AFTER trigger for assigning sequence values to the PK. The common solution is to use a BEFORE trigger.
See example in: How to create id with AUTO_INCREMENT on Oracle?
CREATE SEQUENCE IMOVEL_TRIGGER_SEQ START WITH 1;
Trigger definition:
CREATE OR REPLACE TRIGGER IMOVEL_TRIGGER
BEFORE INSERT ON IMOVEL
FOR EACH ROW
BEGIN
SELECT IMOVEL_TRIGGER_SEQ.NEXTVAL
INTO :new.GEOCODIGO_IMOVEL
FROM dual;
END;
Use Oracle database and need to store all rows in lower case. All INSERTand UPDATE should insert and update rows with lower case data. I wrote a trigger to do it. Read a lot of about table mutation with triggers.Very confused, will this code raise mutate error or not. Please, write here, if it will.
create or replace trigger employee_name
before update or insert on employee
for each row
begin
/* convert character values to lower case */
:new.lastname := lower( :new.lastname );
:new.firstname :=lower( :new.firstname );
end;
/
The trigger will not raise a mutating table error as you're not selecting from the table your trigger is on; you're only reassigning values, which is what triggers are designed to do.
It's worth noting that this can also be accomplished with a CHECK constraint, which will force everyone updating or inserting into the table to lower case everything:
alter table employee_name
add constraint chk_emp_name_lastname
check ( lastname = lower(lastname) )
Whilst this will increase update/insert times (not necessarily more than using a trigger) it doesn't silently change data being entered but instead complains that the data is incorrect. This is sometimes a better approach.
this code not raise exception
because you not try select from table which trigger executing
This should not raise any errors. Mutate errors occur when you read (or modify) some other rows from BEFORE trigger.
NOTE: there are situations where BEFORE trigger is fired more that once for the SAME row.
In order to guarantee consistency Oracle would have to justify, that your trigger body is "idempotent": e.i. each execution on the same input gives the same result.
In some cases when a record is inserted into a table, it should be split into few records, which would be inserted instead.
The logic is written inside an INSERT trigger, which fires before the INSERT operation. Inside of this trigger I am trying to execute an INSERT statement, which subsequently causes a recursive call of the trigger. However I do not want this to happen. I tried to disable a trigger from its body using smth like
execute immediate 'ALTER TRIGGER sale_trigger DISABLE';
But, obviously it is a commit operation and thus it doesnt work from inside of the trigger.
How can I get around this recursive call of the trigger?
Edit I declared my trigger as this declare PRAGMA AUTONOMOUS_TRANSACTION; and now i can run alter statement. However when I disable a trigger from the same trigger - the PLSQL developer stops working. What do I do? :)
Instead of loading the first insert directly into your target table, insert records into a staging table. The trigger with the troublesome logic should be on the staging table. If the logic does not apply, the trigger inserts the row into the target table. If the logic applies, the trigger inserts the row (if necessary?) and fires off whatever additional inserts to the target table are required. Truncate the staging table on regular intervals to keep it small and efficient (but probably not after each trigger operation since this would be less efficient).
In other words: decouple the trigger from the table it is inserting into.
If I have a statement
that updates multiple rows, only the trigger will fire only on the first or last row that is being updated (not sure which one). I need to make a trigger that fires for ALL the records that are being updated into a particular table
Assuming SQL Server, A trigger only fires once per update, regardless of the number of rows that are updated. If you need to carry out some additional logic based on updates to multiple rows you can access the changed records by looking at the INSERTED and DELETED logical tables that are accessible in the context of a trigger.
You have not specified the database .....
In Oracle a trigger can be defined to fire for individual rows and based on the type of transaction:
CREATE OR REPLACE TRIGGER BIUDR_MY_TABLE
BEFORE INSERT OR UPDATE OR DELETE
ON MY_TABLE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
pk PLS_INTEGER;
BEGIN
etc ....
You just need to indicate if your trigger needs to be executed "FOR EACH ROW" or "FOR EACH STATEMENT". Adding one of these two clauses in the trigger definition will tell the DBMS when to execute the trigger (most, but not all, DBMSs support it). If you don't indicate this clause then the DBMS uses the default option which in your case seems to be the FOR EACH STATEMENT option, and that's why your trigger only fires one for each update sentence, regardless of how many rows you are updating