I wrote a trigger that runs before a row is deleted that updates a table summarizing the data from this table. The trigger works well when I delete a single row at a time. However, if I were to delete multiple rows at once with a statement like
DELETE FROM myTable WHERE id BETWEEN 1 and 100;
Will the trigger completely run on the first row before the next row is deleted or will the triggers run all at the same time?
The trigger will completely run for every single row, see Trigger FAQ
A.5.3: Does MySQL 5.6 have statement-level or row-level triggers?
In MySQL 5.6, all triggers are FOR EACH ROW—that is, the trigger is
activated for each row that is inserted, updated, or deleted. MySQL
5.6 does not support triggers using FOR EACH STATEMENT.
That is valid for the currently newest version, MySQL 5.7 too.
from http://dev.mysql.com/doc/refman/5.5/en/trigger-syntax.html
The statement following FOR EACH ROW defines the trigger body; that is, the statement to execute each time the trigger activates, which occurs once for each row affected by the triggering event. In the example, the trigger body is a simple SET that accumulates into a user variable the values inserted into the amount column. The statement refers to the column as NEW.amount which means “the value of the amount column to be inserted into the new row.”
The delete transaction will only occur once, but then for every row affected by query the trigger will occur
Related
I have a table on a mysql 5.7 db, containing say athletes with their mean, max, avg times in a specific sport. I have another table that lists some calculated statistics based on those values.
I managed to do the calculcations that end up on the second using stored procedures. I use as input parameter to the stored procedure the athlete's name.
So when in the first table, an athlete is inserted (with his/her avg/min/max times) or his/her values are updated and I run the stored procedure, the later updates the statistics table.
My question is how to achieve the same result with triggers?
I guess it is feasible/easy to update the entire table on each insert or update of the first table. What would be more efficient performance-wise, would be on each :
INSERT into table1 values (..) where athlete_name="John Do"
(...)
ON DUPLICATE KEY UPDATE (...)
Run a trigger in the pseudocode form :
INSERT into statistics_table values (..) where athlete_name="John Do"
ON DUPLICATE KEY UPDATE (...)
How can the the athlete_name="John Do" be passed to the trigger dynamically, to avoid update the entire statistics table?
You cannot pass any parameters to a trigger and the insert statement does not support the where clause either.
Having said this, a trigger can pick up the user's name from the record being inserted / updated / deleted using NEW.athlete_name or OLD.athlete_name (whichever is required) and use that to call a stored procedure:
Within the trigger body, the OLD and NEW keywords enable you to access
columns in the rows affected by a trigger. OLD and NEW are MySQL
extensions to triggers; they are not case-sensitive.
In an INSERT trigger, only NEW.col_name can be used; there is no old
row. In a DELETE trigger, only OLD.col_name can be used; there is no
new row. In an UPDATE trigger, you can use OLD.col_name to refer to
the columns of a row before it is updated and NEW.col_name to refer to
the columns of the row after it is updated.
A column named with OLD is read only. You can refer to it (if you have
the SELECT privilege), but not modify it. You can refer to a column
named with NEW if you have the SELECT privilege for it. In a BEFORE
trigger, you can also change its value with SET NEW.col_name = value
if you have the UPDATE privilege for it. This means you can use a
trigger to modify the values to be inserted into a new row or used to
update a row. (Such a SET statement has no effect in an AFTER trigger
because the row change will have already occurred.)
You can create triggers that fire after each insert or update on the parent table (athletes). Within each trigger, you can access the value of column athlete_name on the record that was just created or changed, and then invoke your stored procedure using CALL().
Here is a code sample for such an INSERT trigger :
CREATE TRIGGER athletes_upd AFTER INSERT ON athletes
FOR EACH ROW
BEGIN
CALL my_procedure(NEW.athlete_name);
END;
UPDATE trigger :
CREATE TRIGGER athletes_upd AFTER UPDATE ON athletes
FOR EACH ROW
BEGIN
CALL my_procedure(NEW.athlete_name); -- or maybe OLD.athlete_name ?
END;
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.
MYSQL:
I have a table with a AFTER INSERT ON trigger on it.
I have inserted a million rows in the table and the trigger code successfully gets executed and updates 10 other tables.
Now, I have deleted the data from the 10 children tables but the million rows in the parent table is retained.
Can I manually simulate that trigger code for those million records in the parent table to reinsert them in the 10 children tables...???
No you can't. Only actual, successful INSERTs trigger before/after insert triggers.
You can use the "AFTER INSERT" trigger like an "AFTER UPDATE", then you can make a safe update where you don't harm anything, this way you can re insert the rows.
Don't forget to delete "AFTER UPDATE", and restore de respective "AFTER INSERT".
I have an industrial system that logs alarms to a remotely hosted MySQL database. The industrial system inserts a new row whenever a property of the alarm changes (such as the time the alarm was activated, acknowledged or switched off) into a table named 'alarms'.
I don't want multiple records for each alarm, so I have set up two database triggers. The first trigger mirrors each new record to a second table, creating/updating rows as required. The second table ('alarm_display') has the 'Tag' column set as the primary key. The 'alarm' table has no primary key. The code for this trigger is:
CREATE TRIGGER `mirror_alarms` BEFORE INSERT ON `alarms`
FOR EACH ROW
INSERT INTO `alarm_display` (Tag,...,OffTime)
VALUES (new.Tag,...,new.OffTime)
ON DUPLICATE KEY UPDATE OnDate=new.OnDate,...,OffTime=new.OffTime
The second trigger should execute after the first and (ideally) delete all rows from the alarms table. (I used the Tag property of the alarm because the Tag property never changes, although I suspect I could just use a 'DELETE FROM alarms WHERE 1' statement to the same effect).
CREATE TRIGGER `remove_alarms` AFTER INSERT ON `alarms`
FOR EACH ROW DELETE FROM alarms WHERE Tag=new.Tag
My problem is that the second trigger doesn't appear to run, or if it does, the second trigger doesn't delete any rows from the database.
So here's the question: why does my second trigger not do what I expect it to do?
The explanation can be read here: http://dev.mysql.com/doc/refman/5.0/en/stored-program-restrictions.html
Within a stored function or trigger,
it is not permitted to modify a table
that is already being used (for
reading or writing) by the statement
that invoked the function or trigger.
This is your problem and your trigger ends with error #1442.
The table alarms is already being used by the statement that invoked your trigger (the insert). This essentially means you cannot modify alarms with a delete trigger.
Cheers!
I would like to create an after update trigger that runs only once even if multiple rows have been updated.
Nope:
http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-23-5-1-11
In MySQL 5.0, all triggers are FOR
EACH ROW—that is, the trigger is
activated for each row that is
inserted, updated, or deleted.
However, see the following hack.