I have the below trigger:
CREATE Trigger instructor_expertise on CourseSections
After Insert
As Begin
......
If (Not Exists(Select AreaName From AreasOfInstructor Where (InstructorNo = #InstructorNo AND AreaName = #AreaName)))
Begin
RAISERROR('Course not in instructors expertise', 16, 1)
rollback transaction
End
GO
My question is, does 'rollback transaction' remove the row?
What if it's 'For Insert' instead, does 'rollback transaction' remove the row in that case?
Thanks!!!
Your INSERT statement always runs in a transaction - either you've explicitly defined one, or if not, then SQL Server will use an implicit transaction.
You're inserting one (or multiple) row into your table. Then - still inside the transaction - the AFTER INSERT trigger runs and checks certain conditions - typically using the Inserted pseudo table available inside the trigger, which contains the rows that have been inserted.
If you call ROLLBACK TRANSACTION in your trigger, then yes - your transaction, with everything it's been doing, is rolled back and it's as if that INSERT never happened - nothing shows up in your database table.
Also: FOR INSERT is the same as AFTER INSERT in SQL Server - the trigger is executed after the INSERT statement has done its job.
One thing to keep in mind (which a lot of programmers get wrong): the trigger is fired once per statement - NOT once per row! So if you insert 20 rows at once, the trigger is fired once and the Inserted pseudo table inside the trigger contains 20 rows. You need to take that into account when writing the trigger - you're not always dealing with just a single row being inserted!
no it is not possible because when their is no row exist then it will go in begin block ...
Related
Sooo, I´m writing in MySQL a trigger to count how many attempts of an insert query happened (even failed attempts) but so far nothing.
If the insert is succesful, the variable 'attempts' increases its value by one. But when the insert query fails (because you tried to insert something ilogical) the trigger makes a rollback and 'attempts' doesn't increase.
How to avoid the rollback? Or how to outsmart it so 'attempts' will increase?
Here is my code:
CREATE TABLE myData (myValues INT);
SET attempts =0;
DELIMITER |
CREATE TRIGGER countingAttempts BEFORE INSERT ON myData FOR EACH ROW
SET #attempts = #attempts+1;
DELIMITER ;
INSERT INTO myData VALUES(10); /* It works, attempts becomes 1*/
INSERT INTO myData VALUES (X); /* The insert query fails because 'myValues' is INT and X is not an INT, attempts should become 2, but the trigger rollsback and attemps doesn´t change*/
It seems like you are trying to count attempted inserts that result in errors due to bad data (presumably with STRICT_TRANS_TABLES mode enabled).
There is no "rollback", since no insert was actually done. The trigger simply isn't executed by the point the error is detected.
(Though if you are inserting multiple rows, the trigger will be executed for initial correct rows before the erroneous one, so you will see the variable increased in that case.)
You could experiment with disabling STRICT_TRANS_TABLES and doing some validation in your trigger instead, but that's going to have an effect on all other tables and update statements too, so I wouldn't recommend it.
The other option I see is to not do inserts from the client, but instead call a stored procedure to do the insert; that gives you a chance to increment your counter in the stored procedure whether the insert works or not.
I have implemented a typical "audit log" trigger in Mysql version 5.5. I use AFTER INSERT to copy inserted rows from table user into my 'audit_log' table.
So for this sequence:
BEGIN;
insert into user (name) values ('joe');
<--trigger fires, adds new row to audit_log table-->
COMMIT;
Then I get a new row in 'audit_log' with 'joe'. Great.
However, it appears that the results of my trigger are applied even if the insert which fired the trigger is aborted by its enclosing transaction.
So for this sequence:
BEGIN;
insert into user (name) values ('mary');
<--trigger fires, adds new row to audit_log table-->
ROLLBACK;
I STILL end up with a new row 'mary' in audit_log, which refers to data that was never committed to my user table. This seems pretty clearly wrong.
It appears that trigger updates performed in Postgres execute within the original transaction, which is what I would expect to happen. Anyone have experience with this in MySQL? Perhaps there is a setting I have wrong?
I note that this question implies that all updates happen in the original transaction. However, the answer refers to the Mysql manual page on triggers, which in fact has no mention of "transation" at all.
Turns out my audit table was using the MyISAM engine which of course prevented it from obeying the transaction properly.
My colleague says I owe Larry Ellison an apology.
Larry, I'm sorry I doubted the transactional semantics of Mysql.
When I insert data in db I have to compare the current record with the previous one. If neccassary, some values of the current record needs to be modified.
I've tried some pieces of SQL like below, but all give SQL errors. This one gives me an error says that I select more than 1 records.
DELIMITER $$
CREATE
TRIGGER set_moment_display
BEFORE INSERT ON data
FOR EACH ROW
BEGIN
DECLARE moment DATETIME;
SELECT press_moment_1 INTO moment FROM data LIMIT 1;
IF moment > NEW.press_moment_1 THEN SET NEW.press_moment_1 = moment;
END IF;
END$$
DELIMITER ;
How do I achieve what I've described above.
The problem here is that, since a SQL database has no implicit concept of row ordering (you supply the ordering criteria on every query), there is no "previous" row for the trigger to look at. The "previously inserted row" has no meaning in the context of an insert trigger.
Suppose for a moment that it did and there were several processes inserting rows in the table. When the trigger fired for process #1's insert, which row is the "previous" row? The one previously inserted by process #1? Suppose the chronologically "most recent" row was actually inserted by process #3?
If you need to do this it cannot be done in a trigger unless you can use a know key value to identify the row you understand as "most recent". Otherwise it must be handled in the application that is doing the inserts.
You can use the alias "OLD."
You can refer to columns in the subject table
(the table associated with the trigger) by using the aliases OLD and NEW.
OLD.col_name refers to a column of an existing row before
it is updated or deleted. NEW.col_name refers to the column
of a new row to be inserted or an existing row after it is updated
UPDATE
Jim Garrison properly pointed up to me the mistake, "BEFORE INSERT" doesn't have "OLD." values, this alias works only for UPDATE and DELETE.
I have database partially created and edited by an external CRM where certain tables have multiple (at most 2) after triggers on them. This is due to 1 trigger being auto generated by the CRM (over which I have limited control) and the other containing my code.
The CRM trigger generates the primary key for the datarow inserted. My trigger needs to access that primary key in order to write it to another table as foreign key. I use
Select #id=max(id) from mytable
since Scope_Identity did not produce the desired result somehow.
This worked until I let the CRM recreate the table and its own trigger. The maximum id of that table selected by my trigger seemed to always be actual_id - 1.
When I altered my trigger using the same code it has always had the procedure worked again.
My question is:
Does SQL server (I am using SQL Server 2008) set its trigger order by creation time?
And:
Is
sp_settriggerorder #triggername='mycustomtrigger', #order='Last', #stmttype='INSERT'
going to change this permanently or do I have to call that procedure again, every time the CRM recreates its trigger? (using DROP and CREATE, not ALTER)
Hope the answers to that will help someone looking at the same issue.
Regards
It's not documented, but I believe that the LAST setting will stay with a trigger, provided it is not modified. (Contrariwise, it is documented that a trigger will lose this setting if it is modified). However, it seems to work:
create table T (ID int not null)
go
create trigger T_T1 on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
go
create trigger T_T2 on T
after insert
as
RAISERROR('T2',10,1) WITH NOWAIT
go
create trigger T_T3 on T
after insert
as
RAISERROR('T3',10,1) WITH NOWAIT
go
insert into T(ID) values (1)
go
sp_settriggerorder 'T_T2','Last','INSERT'
go
insert into T(ID) values (2)
go
drop trigger T_T1
go
create trigger T_T1 on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
go
insert into T(ID) values (3)
Results:
T1
T2
T3
(1 row(s) affected)
T1
T3
T2
(1 row(s) affected)
T3
T1
T2
(1 row(s) affected)
As to your first question, however:
Does SQL server (I am using SQL Server 2008) set its trigger order by creation time?
It also appears to, but I would not rely on that. sp_settriggerorder is the only place where any ordering is documented.
Finally, as mentioned in my comment, I wouldn't rely on your current Select #id=max(id) from mytable method - it could be broken for a number of reasons, but the most important is that a trigger is fired once per method, and may fire in response to multiple rows, so you ought to write triggers to use the inserted pseudo-table instead (and expect it to contain 0, 1 or multiple rows).
I have an insert trigger which takes a set of column values from rows in table A and inserts some of them in table B and remaining in table C. I need this operation to be a transaction wherein if there is some error whilst data is inserted in table B and not C, the entire insertion operation should be rolled back.
I studied the manual and it says at the last of this page that transaction is not allowed in triggers
Is there a way to achieve what I want in mysql.
Yes you can, but how you do it depends on your version.
First of all, triggers are themselves transactional; in your situation, you have an insert trigger that performs two further inserts. If one of those fails, you will get your desired effect.
Consider the following example:
CREATE TABLE a (colA INT);
CREATE TABLE b (colB INT);
CREATE TABLE c (colC INT);
delimiter :
CREATE TRIGGER testtrig BEFORE INSERT ON a
FOR EACH ROW BEGIN
INSERT INTO b(colB) VALUES(NEW.colA);
INSERT INTO c(banana) VALUES (NEW.colA); -- note the faulty column name
END;:
delimiter ;
Now, when I run an insert that fails, this happens:
mysql> INSERT INTO a VALUES (5);
ERROR 1054 (42S22): Unknown column 'banana' in 'field list'
mysql> SELECT * FROM a;
Empty set (0.00 sec)
This matches your desired result.
More generally, if you have logic you can use to validate your data before attempting the insert, you can fail the trigger in different ways:
In MySQL 5.5, you can use the SIGNAL mechanism to raise an error from your trigger, thus causing it to fail the whole insert.
Prior to MySQL 5.5, you can generate a deliberate error to fail the trigger.
I'm guessing you're using 5.0 from the link in your question, so if you need to, you can perform a deliberate error, for example deliberately insert into an invalid column, to fail a trigger. However, the situation you describe in your question is already handled transactionally, as described at the start of my answer.
You get what you asked for by default -- any error in a trigger causes the statement to fail. So if there is a transaction on the statement, you get a rollback of the data to just before that statement. If there is no transaction, then you don't.
Which is probably why creating or ending a transaction is not allowed in a trigger.
So no need for a stored procedure. In fact, the stored procedure you call from the trigger might cause an error if it tries to create a transaction.
But feel free to use a stored procedure to start a transaction before doing the action that causes the trigger.