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.
Related
I've created a trigger (never done it before).
My goal is:
WHEN one or more rows are INSERTED into the table 'userlite'
IF there are rows with 'IdLite' as in the new row inserted in the table 'litedetails'
THEN add a row to the table 'informations' for each row counted.
The new rows data fields will be:
IdUser -> from new row inserted into the table 'userlite'
IdLite -> it's the same on new row inserted into the table 'userlite' and into rows selected from the table 'litedetails'
IdEvent -> from rows selected
I used the code below to create the trigger
DELIMITER $$
CREATE TRIGGER after_newuserlite
AFTER INSERT ON userlite
FOR EACH ROW
BEGIN
IF (
(
SELECT COUNT(*)
FROM litedetails
WHERE IdLite = NEW.IdLite
) > 0
) THEN
INSERT INTO informations (IdUser, IdLite, IdEvent)
SELECT NEW.IdUser AS IdUser, IdLite, IdEvent
FROM litedetails
WHERE IdLite = NEW.IdLite;
END IF;
END;
$$
I've tested it and all seems to work but I'm worried for my inexperience, so my questions are:
1) Is there anything that can cause my trigger to fail?
2) what happens if the trigger fails?
3) If the trigger fails the query who started the trigger will mantain its effects?
As per comment: when using tables that support transactions, the triggers are then part of the statement. If a trigger fails, it causes the query that triggered it to fail as well, which causes a rollback. This applies to InnoDB and TokuDB storage engines.
For MyISAM, which isn't transactional engine, the trigger might error out but it won't cause a rollback (because it isn't supported by that storage engine).
Triggers can fail due to many reasons, just like regular queries can, but if they fail - you will receive an error message / notification that will let you act upon it (notify the user about failure, log the message, try again etc.).
I have a small database (SQLfiddle) that I am designing. I am trying to create a trigger so that when a row is deleted from downtime, a matching history line gets added to downtimeHistory:
CREATE TRIGGER `announce`.`downtime_BEFORE_DELETE` BEFORE DELETE ON `downtime` FOR EACH ROW
BEGIN
INSERT INTO downtimeHistory (serviceName, startTime) VALUES(OLD.serviceName, OLD.startTime);
END
However, MySQL doesn't care for this trigger. It allows me to create it, and it appears to be functioning correctly, but it throws an error each time the trigger is executed:
1 row(s) affected, 1 warning(s): 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly.
This would make sense to me if I were trying to do something with the id or endTime columns, but updating startTime shouldn't matter since it just provides a default value.
So...why is it throwing this error? How do I stop it? Should I just ignore it?
Your trigger inserts into the downtimeHistory table. The id column is AUTO_INCREMENT.
The error message tells you that this combination is not allowed.
The INSERT INTO.... statement implicitly causes the ID value to be set.
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 ...
I'm trying to insert rows into a table via a trigger or stored procedure without writing any data to the binary log. Is this possible? I know that for normal connections you can set SQL_LOG_BIN=0 to disable binary logging for the connection, but I haven't been able to get that to work for triggers. If there's a way to do this with a federated engine table, that would also be OK.
edit:
I can almost accomplish this via a stored procedure:
CREATE PROCEDURE nolog_insert(`id` INT, `data` blob)
BEGIN
SET SESSION SQL_LOG_BIN = 0;
INSERT INTO `table` (`id`, `data`) VALUES (id, data);
SET SESSION SQL_LOG_BIN = 1;
END
I insert a record by calling the procedure from the mysql prompt:
call nolog_insert(50, 'notlogged');
As expected, the record (50, 'notlogged') is inserted into the table, but is not written to the binary log.
However, I want to run this procedure from a trigger. When using a trigger as follows:
create trigger my_trigger before insert on blackhole_table for each row call nolog_insert(new.id, new.data);
The data is both inserted to the table and written to the binary log.
If you run statement based replication triggers are executed on both the master and the slave but not replicated. Perhaps that could solve your problem.
Other than that, it's not allowed to change sql_log_bin inside a transaction so I would say that there is no good way to have the effects of a trigger not replicated when using row based replication.
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.