I'm having a little issue with a trigger in a MySQL database. I have a DB with two tables: "tasks" and "files". The "tasks" table have a field which is a foreign key of the primary key from the "files" table. It also sometimes may be null.
What I'm trying to acomplish is to delete in the first place a row in the "tasks" table, and after that delete the corresponding row in the "files" table using a trigger.
This is the trigger I'm using right now:
DELIMITER //
CREATE TRIGGER after_delete_file AFTER DELETE ON tasks
FOR EACH ROW
BEGIN
DECLARE fileId int;
SELECT file INTO fileId FROM tasks WHERE id=old.id;
DELETE FROM files WHERE id=fileId;
END;//
DELIMITER ;
The field "file" in the "tasks" table is the one containing the foreign key. In the examples I've been running, that field has never been null.
The problem is that the select statement always returns null. The delete statement that triggers this trigger goes fine, but the row in the "files" table is never deleted. I've tried to insert the "fieldId" variable on a testing table, and it's always saving a null value.
Is there any problem on that trigger? Maybe I'm trying to do something merely impossible?
All the help is much appreciated :)
Since it should be looping over each deleted row, why would this not work?
DELIMITER //
CREATE TRIGGER after_delete_file AFTER DELETE ON tasks
FOR EACH ROW
BEGIN
DELETE FROM files WHERE id=old.file;
END;//
DELIMITER ;
If that doesn't work, could try this:
DELIMITER //
CREATE TRIGGER after_delete_file AFTER DELETE ON tasks
FOR EACH ROW
BEGIN
DELETE FROM files INNER JOIN tasks ON files.id=tasks.file WHERE tasks.id=old.id;
END;//
DELIMITER ;
but I don't think that should be necessary.
AFTER delete means that the data is deleted, of course you can't find it. Try creating the trigger for BEFORE delete.
You could also more carefully use all of the old values rather than selecting from the table that was deleted from.
Related
I am building a nba_players table in MySQL. It has a column for players height in inches (pl_ht_inches). It also has a column (pl_ht_feet) where it lists a players height in feet. For example nba_player Michael Jordan has pl_ht_feet = '6-6' and the pl_ht_inches is updated using the following statement:
update nba_players ab
inner join
feet_hieght_tb bc on ab.pl_ht_feet = bc.hieght
set
ab.pl_ht_inches = bc.total_inches
this statement updates nba_players.pl_ht_inches using table 'feet_hieght_tb' which has two columns
hieght - hieght varchar(4) with data like '6-6' which is imperial measurement
total_inches int(3) which lists height in inches. for example hieght '6-6' would be total_inches = 78.
I then used a trigger with the update statement. I subsequently found out that you cant reference the same table as the insert statement in the trigger for that table. The trigger reads as follows:
delimiter $$
create trigger
after_nba_players_insert
after insert on nba_players
for each row
begin
update nba_players ab
inner join feet_hieght_tb bc on ab.pl_ht_feet = bc.hieght
set ab.pl_ht_inches = bc.total_inches;
end$$
delimiter ;
this produces error 1442 when I attempt to insert new data into nba_players which means the trigger is updating the same table as as the insert into the nba_players table.
So then I tried to trigger the update by creating a stored procedure where I included the same update statement that i used for the trigger. I can call the procedure manually after doing an insert into nba_players and it works fine. But if I use the stored procedure in the trigger I get error 1442.
I dont want to have to remember to call the procedure every time I do an insert into nba_players and would like to automate this task if I could.
Is there an alternative to using triggers or stored procedure to update a column using data from a different table.
I have been researching generated columns but they dont allow select statements. What I mean by this is that the nba_players.pl_ht_inches is a foreign key to the table feet_hieght_tb that references total_inches where nba_players.pl_ht_feet = feet_hieght_tb.height. I would rather the foreign key update on its own rather than having to do this manually.
Any assistance will be greatly appreciated. Cheers.
I have several tables in the database: Users, Profiles, Articles
I also have one table called Changes, which is used for administrative purposes. This table consists of id, table_name, and date_created.
What I need to do is whenever something is added, deleted or updated in a regular table (Users, Profiles, Articles), create a new row in the Changes with the name of the updated table and the current timestamps.
I've been browsing for a while and tried many different methods, but nothing really worked. I know the solution should be very simple, may be someone can help me. Thank you for your time.
So in this case you need 9 trigger 3 for each of the regular table after insert, after update, after delete
Here is for one table you can write for the others
When you insert on Users
delimiter //
create trigger log_user_insert after insert on Users
for each row
begin
insert into Changes (table_name,date_created) values ('Users',now());
end; //
delimiter ;
When update happens on Users
delimiter //
create trigger log_user_update after update on Users
for each row
begin
insert into Changes (table_name,date_created) values ('Users',now());
end; //
delimiter ;
When delete happens on Users
delimiter //
create trigger log_user_update after delete on Users
for each row
begin
insert into Changes (table_name,date_created) values ('Users',now());
end; //
delimiter ;
I would suggest to add a column called action in the table Changes and to insert each action name as well i.e. insert,update and delete.
You need to create an update, insert and delete trigger on each of the data tables:
CREATE TRIGGER upd_changes_users BEFORE UPDATE ON Users
FOR EACH ROW
BEGIN
INSERT INTO changes (table_name, date_created) VALUES ('users', NOW());
END;
This code assumes, that the id column in changes is auto_generated. You might also want to consider including a type column in the changes table (to differentiate between insert, update and delete).
I need to update a table with pre-calculated values from tables where data can be added/updated/deleted.
I could use
insert into precalculated(...)
select ... from ...
on duplicate key update ...
to add/update the pre-calculated table but is there an optimized method to delete the obsolete rows ?
I think you should create a stored procedure that deletes the data of your related tables if and only if the records fulfill a condition.
There's not enough information in your question to design the procedure, but I can give you a little example:
delimiter $$
create procedure delete_orphans()
begin
declare id_orphan int;
declare done int default false;
declare cur_orphans cursor for
select distinct d.id
from data as d
left join precalculated as p on d.id = p.id
where p.id is null;
declare continue handler for not found set done = true;
open cur_orphans;
loop_delete_orphans: loop
fetch cur_orphans into id_orphan;
if done then
leave cur_orphans;
end if;
delete from data where id = id_orphan;
end loop;
close cur_orphans;
end$$
delimiter ;
This procedure will delete every row in the data table that does not have at least one related row in the precalculated table.
Of course, this approach might be inneficient, because it will delete the rows one by one, but as I said this is only an example. You can customize it to fit your needs.
You can call this procedure from a trigger if you want (with call delete_orphans()).
Hope this helps.
Since you are always adding or updating rows that exist in these other tables, and you want to remove any rows that don't exist, why don't you just :
DELETE FROM precalculated
insert into precalculated(...)
select ... from ...
on duplicate key update ...
Always starting clean means you don't have to worry about orphans later.
You could add triggers for insert, delete and update on the main tables that maintains precalculated.
When inserting or updating the same code can be used to calculate the values and issuing a replace into precalculated (...) values (...)
When deleting it's probably the same, with the addition that you'll also delete rows from precalculated that are orphans. Be smart here and use values from the original delete to query precalculated for orphans instead of doing a table scan.
I may have found my solution using rename.
so basically, I will do a simple insert select to the temporary table and then
rename precalculated to precalculated_temprename, precalculated_temp to precalculated, precalculated_temprename to precalculated_temp;
truncate precalculated_temp;
need some tests but it seems the rename operation is fast and atomic.
It is possible to use a version control system with mysql databases?
Or, is there a version control system already implemented?
I want to say e.g.: SELECT foo FROM bar WHERE version = X
Whereeby version is a mysql internal colum with last update date.
Very late response... similar to Ruben's suggestion, I have setup triggers to update a version_control table to increment version number every time there is an INSERT, UPDATE & DELETE.
I laid out the steps on my site mradamfrancis.tumblr.com
** update **
I’ve decided to use triggers to assist with version control. Here’s how…
I have a table containing players, if there are changes (INSERT, DELETE, or UPDATE) I want to increment the version number in my version_control table.
This is how the version_control table looks:
version_id (key), table_name (varchar), version (integer)
I then create 3 triggers on the players table, one for INSERT, DELETE & UPDATE.
INSERT:
delimiter //
CREATE TRIGGER `player_table_INSERT` AFTER INSERT ON `players`
FOR EACH ROW BEGIN
UPDATE version_control SET version=version+1 WHERE table_name=’players’;
END;//
delimiter ;
DELETE:
delimiter //
CREATE TRIGGER `player_table_DELETE` AFTER DELETE ON `players`
FOR EACH ROW BEGIN
UPDATE version_control SET version=version+1 WHERE table_name=’players’;
END;//
delimiter ;
UPDATE:
delimiter //
CREATE TRIGGER `player_table_UPDATE` AFTER UPDATE ON `players`
FOR EACH ROW BEGIN
UPDATE version_control SET version=version+1 WHERE table_name=’players’;
END;//
delimiter ;
** I have additional SQL statements in the FOR EACH section of the trigger, hence I’ve used delimiter (1st line and last line) along with BEGIN & END.
You could also define extra tables for logging. For instance if you already have a table news, you can duplicate it as news_log. then add columns for logging data such as: modified date, action (update, delete, add) and so on.
next you define triggers on the original tables that will insert the data into your logging table. for instance when you update a record in you news table the news_log_trigger that you define is executed and a new record is inserted into new_log with the action value "UPDATE" and the current date as modified date.
for more info on mysql triggers:
http://dev.mysql.com/doc/refman/5.0/en/triggers.html
In case you want to do this for every project you can problably write a generic stored procedure to do the actual logging. that way you can reuse it and you only have to define the triggers and logging tables.
I'm using MySql, for my Database and I have a table "Person". Amongst other thing, it contains a relation to itself "Person_MarriedTo" stored as a foreigh_key;
This relation can be null since it's not everybody that's married. What I want to do is that this field can be updated automatically.
if I add A married to B, then B married to A
if B deivorce from A, then A is also divorced from B
I thought triggers would be the way to go, but I can't get my code to work the way I want it to. Any insights would be much appreciated.
Here is my code for the trigger:
USE `mydb`;
DELIMITER $$
CREATE TRIGGER `Person_BUPD` BEFORE UPDATE ON Person FOR EACH ROW
BEGIN
IF OLD.Person_MariedTo != NULL THEN
UPDATE Person SET Person_MariedTo = NULL WHERE UID_Person = OLD.Person_MariedTo;
END IF;
UPDATE Person SET Person_MariedTo = OLD.UID_Person WHERE UID_Person = NEW.Person_MariedTo;
END$$
What you are trying to do is not possible using a trigger.
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. You need to do this some other way.
Source
See here:
MySQL - Trigger for updating same table after insert