MySQL throws a warning when executing a "before delete" trigger - mysql

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.

Related

I don´t want trigger to rollback

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.

MySql cannot update a DATETIME field - no record found

I have a table in MySql 5.6.10 defined as this:
When I do a select query (from HeidiSQL client) for a particular record filtering on the id_failed_delivery_log column, the record is found successfully, but when I use the same filter for the UPDATE command, then no record is found and no error reported:
When I update a different column using the same filter, the update works and I can see the updated value. Then there is an issue with an update to this particular column.
I've also tried updating with a time function instead of hard-coded date value, for example with the now() function but I still get the same result and the record is not found.
Could it be caused by the 'Default' value is set to CURRENT_TIMESTAMP?
After further investigation I found the reason why I couldn't update that field. I was not totally familiarized with the database definition and I found that there was a trigger in the same schema that forced to keep the date_created column with the same value:
SET #OLDTMP_SQL_MODE=##SQL_MODE, SQL_MODE='STRICT_ALL_TABLES';
DELIMITER //
CREATE TRIGGER `failed_delivery_log_before_update` BEFORE UPDATE ON
`failed_delivery_log` FOR EACH ROW BEGIN
SET NEW.date_created = OLD.date_created ;
END//
DELIMITER ;
SET SQL_MODE=#OLDTMP_SQL_MODE;
I removed this trigger temporarily in order to test. Once removed, the updated worked fine. The trigger execution was not reported in the SQL client, so it was difficult to find out its execution.

SQL Server - After Insert/ For Insert - Rollback

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 ...

Can I use transaction like capability in MySQL trigger

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.

Last Inserted Row info without Auto-Incrementing PK

I've got a number of tables that "share" a single auto-incrementing primary key - this is accomplished via a trigger on insert which looks like this:
FOR EACH ROW
BEGIN
INSERT INTO master (time) VALUES (NOW());
SET NEW.id = LAST_INSERT_ID();
END
This produces the PK for the just inserted row. This does, however, create the problem that I can't seem to figure out what that ID was. last_insert_id obviously returns nothing as the above statement wasn't executed on what's considered "the current connection".
Is there a way to access the most recently inserted row on a connection without an auto-incrementing primary key?
Update: As a temporary(?) measure I've removed the trigger and now generate the ID by making the insert to master within my model. Just seems like it would be nicer if I could somehow return the value that the trigger set.
The doc does say, "For stored functions and triggers that change the [LAST_INSERT_ID] value, the value is restored when the function or trigger ends, so following statements do not see a changed value."
Try a stored procedure, which can do your two INSERTS and return the assigned ID.
Or, give up on doing things the "Oracle way", drink the MySql Kool-Aid, and just use an auto-incrementing id on the table.