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;
Related
I am trying to create a trigger to simultaneously update a different table than the one I have updated, with the same data.
I have two different database with the same tables and i'm trying to sync them, when i insert, update or delete data from one, i want to do automaticaly the same to the other table, with triggers.
This is the trigger code:
CREATE DEFINER=`Ivan_test`#`%` TRIGGER `Prueba_Ivan`.`mag_articulos_PI_AFTER_UPDATE` AFTER UPDATE ON `mag_articulos_PI` FOR EACH ROW
BEGIN
IF OLD.Prueba_Ivan.mag_articulos_PI NOT IN (SELECT * FROM ivan_test.mag_articulos_IT) THEN
INSERT INTO ivan_test.mag_articulos_IT
VALUES (new.xempresa_id, new.xarticulo_id, new.xcategoria_id, new.xvisible_web, new.xnovedad,new.xpromocion,new.ximagen_prelim,new.ximagen_amp,new.xtexto1,new.xtexto2,new.xtexto3,new.xtexto4,new.xtexto5);
ELSE
UPDATE ivan_test.mag_articulos_IT SET OLD.ivan_test.mag_articulos_IT = NEW.Prueba_Ivan.mag_articulos_PI;
END IF;
END
but I have this error:
Error Code: 1109. Unknown table 'OLD.Prueba_Ivan' in IN/ALL/ANY subquery
Can someone help me to find the mistake?
Thank you!!
OLD.Prueba_Ivan.mag_articulos_PI
OLD is an alias to the triggered row. Your trigger applies to Prueba_Ivan, which means that OLD and NEW are representing your Prueba_Ivan record before the change, and after it, respectively. This means that when you intend to reference mag_articulos_PI, you will need to do it via OLD.mag_articulos_PI, so remove the tablename from that expression.
OLD.ivan_test.mag_articulos_IT
As mentioned in the previous section, here OLD is an alter-ego of the updated Prueba_Ivan record, you do not need it in order to reference ivan_test.
Further explanation
An expression of the form of
a.b.c
reads as follows:
In database a, table b, column c. When you do something of the like of
OLD.t.c
it reads: In the OLD database, table t, column c.
I'm using a trigger that triggers AFTER INSERT, when the user inserts data into the the table i want to be able to use the information that was just inserted into the table in my trigger.
Is there any way to do this?
Use the special variable new to get the data for the new added row(s), as described in the manual at 13.1.22 CREATE TRIGGER Statement
Within the trigger body, 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.
I am trying to replicate the Username field in my database. Specifically I was looking to add/remove the Username across different tables, whenever the row that contains Username is added inside UserDatabase. To that end, I was thinking of using the trigger mechanism.
I am thinking along the lines of:
CREATE TRIGGER 'addUsername' AFTER INSERT ON UserDatabase FOR EACH ROW
IF (UPDATE(Username))
BEGIN
INSERT INTO anothertable (Username) VALUES ('NewUser');
END
My question is that is how do I capture the updated Username from UserDatabase and replicate it into NewUser? And also, is there a way to remove FOR EACH ROW as I only want the loop to run once?
Thanks!
From the manual:
Within the trigger body, 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.
And no, you can not remove for each row and I think you misunderstood it a little. There actually is no loop. for each row refers to multiple rows in your insert or update statement. When you add multiple users with one insert statement, you want all of them replicated, right? Not just one.
Then you should note, that there's a difference between single-quotes and backticks. Backticks should be used, if a column or tablename or whatever includes characters that shouldn't be there, like a space, or if the name is actually a reserved keyword. Single-quotes, like you used them for the trigger name are used to tell MySQL it's a string.
Your trigger should look something like this:
CREATE TRIGGER 'addUsername' AFTER INSERT ON UserDatabase
FOR EACH ROW
INSERT INTO anothertable (Username) VALUES (NEW.Username);
For an update statement, you have to create another trigger.
CREATE TRIGGER items_
ON test
after update
AS
begin
INSERT INTO test2(id,namecan)
SELECT id,namecan from test
end
I Have tried with trigger but i didnt get any results so please help me how to deal with it
methods involving two stored procedures are also welcome
You could do it with a stored procedure, but I would use a trigger.
With a stored procedure, I'd perceive you using a cursor on the otherDatabase table to read through the records and compare each with the values in Table1 to determine whether Table1's data needed to be written to Table2, and if so, to do it.
With a trigger, I would simply update the data in Table1 by whatever means, without concerning myself with what the overwriting data is, and in the trigger,
use the old and new values using the ##Inserted & ##Deleted (system) tables to determine if the old values (##Deleted) needed to be written to Table2.
If you don want to use triggers, you can go for this concept..
Let there is a form from which you are inserting the values on a table say register table. And the action of the form in going to servlet MyServlet. In Myservlet
you can first fetch the data from the Register table and store it into the Rsultset object let rs. And after that insert the rs into a new table.
Please let me know if m not clear to u..
Your trigger syntax is wrong
delimiter //
CREATE TRIGGER items_ after update
ON test
for each row
begin
INSERT INTO test2(id,namecan) values (old.id,old.namecan);
end; //
delimiter ;
So this trigger will make an entry to the test2 table every time you update row in the test table with the old row values from test table.
You should call old.id and old.namecan (i assume that table 'test' have 'id' and 'namecan' field)to get the older data. your trigger body should look like this
begin
INSERT INTO test2(id,namecan) value(old.id, old.namecan)
end
'old' will reference to record which you update.
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.