As you probably know, there's no syntax that modifies a MySQL trigger.
To do that, you need to execute DROP TRIGGER and then re-create it again with the new definition.
What is the right/best way of doing this, considering the following:
You cannot encapsulate these two statements in a transaction as that will be pointless (both DROP TRIGGER and CREATE TRIGGER invoke implicit transactions)
You cannot use LOCK TABLES READ as an error is triggered
Just between your DROP and CREATE TRIGGER, some other session might insert/update/delete row(s) which won't be handled by neither of your new nor old triggers.
When testing before I posted, I overlooked what type of LOCK I'm acquiring, it was READ.
So it seems using WRITE lock does the job:
delimiter $$
LOCK TABLES table1 WRITE $$
DROP TRIGGER IF EXISTS after_insert_on_table1 $$
CREATE TRIGGER after_insert_on_table1 AFTER INSERT ON table1
FOR EACH ROW
BEGIN
...
END
$$
UNLOCK TABLES $$
delimiter ;
So my recommendation is to always use this sequence when updating/modifying triggers.
Related
I want to run a trigger once and only once the first time a condition is satisfied.
To do this I would like to drop the trigger from within the body of the trigger itself. I have two questions: 1) is there a better way than this and 2) will anything weird happen if I drop the trigger inside the trigger body?
This is what I have so far. For context: There's another process running moving things to done and in a particular case it does not write the result so in that case I want to run a script such that when they're all done I want this trigger to read some values another table and then remove the trigger itself so that it doesn't run every single time stuff gets done normally.
CREATE TRIGGER some_trigger AFTER UPDATE ON table_name FOR EACH ROW
SELECT CASE WHEN ((SELECT count(*) FROM table_name WHERE status!='done') = 0)
THEN BEGIN
UPDATE table_name SET result = (SELECT other.result FROM table_name, other WHERE other.id = table_name.id);
DROP TRIGGER some_trigger;
END;
ELSE BEGIN END;
END CASE;
EDIT: also a third question, what does "FOR EACH ROW" mean? I only want the trigger to run once, not once per row. Looking at the docs it seems like "FOR EACH ROW" is not optional.
DROP TRIGGER cannot be performed within a Trigger.
To explain why, firstly, DROP TRIGGER causes an implicit commit, and secondly, commits cannot occur within triggers. Details below:
DROP TRIGGER causes an implicit commit
See (https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html):
The statements listed in this section (and any synonyms for them) implicitly end any transaction active in the current session, as if you had done a COMMIT before executing the statement.
...
Data definition language (DDL) statements that define or modify database objects. ALTER EVENT, ALTER FUNCTION, ALTER PROCEDURE, ALTER SERVER, ALTER TABLE, ALTER VIEW, CREATE DATABASE, CREATE EVENT, CREATE FUNCTION, CREATE INDEX, CREATE PROCEDURE, CREATE ROLE, CREATE SERVER, CREATE SPATIAL REFERENCE SYSTEM, CREATE TABLE, CREATE TRIGGER, CREATE VIEW, DROP DATABASE, DROP EVENT, DROP FUNCTION, DROP INDEX, DROP PROCEDURE, DROP ROLE, DROP SERVER, DROP SPATIAL REFERENCE SYSTEM, DROP TABLE, DROP TRIGGER, DROP VIEW, INSTALL PLUGIN, RENAME TABLE, TRUNCATE TABLE, UNINSTALL PLUGIN.
Commits cannot occur within a trigger:
See (https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html):
The trigger cannot use statements that explicitly or implicitly begin or end a transaction, such as START TRANSACTION, COMMIT, or ROLLBACK. (ROLLBACK to SAVEPOINT is permitted because it does not end a transaction.).
I have two tables like as:
fee_master(id,cTId,feeType,amount,startDate,lastDate,fine_last_date,fine,status)
payroll(id,emId,date,loan,netSalary)
I am trying to create a trigger like as:
DELIMITER $$
DROP TRIGGER IF EXISTS test
DELIMITER $$;
CREATE TRIGGER test
BEFORE DELETE ON fee_master
FOR EACH ROW
UPDATE payroll SET loan=OLD.amount,netSalary=OLD.fine WHERE id=18;
DELIMITER $$;
delete from fee_master where id='18';
When I have run this trigger, the data is deleted from fee_master, but payroll is not updated, also I have tried to insert payroll but not working.Every times the data is deleted from fee_master.
If I change the update and delete query position with trigger then It is ok. Actually, It is not working on trigger operation.
What is the problem ?
Your syntax for UPDATE is incorrect. Multiple assignments are separated by ,, not AND.
UPDATE payroll SET loan=OLD.amount, netSalary=OLD.fine WHERE id=18;
May be you are new on triggering.
According to your question, I recommend you first read basics of triggering from here http://www.sitepoint.com/how-to-create-mysql-triggers/
Remind that, It is a stored process. You do not need to run the trigger every times,I hope you are confuse here. After creating a trigger, You have to run the master query then the trigger automatically run the next operation.
Your code is ok. And the code of Barmar also ok.The main problem your understanding.
Problem is that, I want a trigger that deletes old rows in the same table that the new rows are being inserted into.
MsSQL and oracle can do this,
but looks like mySQL can't,
It allows the trigger to be created, but when it runs it gives the error
"can't update table "tbl" in stored procedure or function/trigger
because it is already used by statemtent whicgh invoked this stored
procedure or function/trigger"
Any work around for this?
Is it planned in future releases?
I have in my database all tables with filed name GHOST, so when GHOST is TRUE this row is like DELETED. So if You want change row after insert maybe use like this:
CREATE TRIGGER my_trigg
BEFORE INSERT ON table
FOR EACH ROW
BEGIN
SET NEW.ghost = TRUE;
END
$$
DELIMITER ;
When I ran the same query in different databases, it works successfully. But in mysql schem it gives error:
#trigger can not be created on system table
My query is:
delimiter //
CREATE TRIGGER `invite` AFTER INSERT ON `Invite_page`
FOR EACH ROW BEGIN
Insert into userpost(userid,url,title,preview,sentiment,time) values(NEW.userid,NEW.url,NEW.title,NEW.preview,NEW.sentiment,NEW.time);
Insert into urlcontent(userid,url,title,preview,sentiment,time) values(NEW.userid,NEW.url,NEW.title,NEW.preview,NEW.sentiment,NEW.time);
END
//
delimiter ;
If I can't, then how can I solve this instead?
UPDATE:
actual error:
#1465 - Triggers can not be created on system tables
Try this
DELIMITER $$
CREATE TRIGGER `invite` AFTER INSERT ON `Invite_page`
FOR EACH ROW
BEGIN
Insert into userpost(userid,url,title,preview,sentiment,time) values(NEW.userid,NEW.url,NEW.title,NEW.preview,NEW.sentiment,NEW.time);
Insert into urlcontent(userid,url,title,preview,sentiment,time) values(NEW.userid,NEW.url,NEW.title,NEW.preview,NEW.sentiment,NEW.time);
END$$
DELIMITER ;
Under the "Restrictions for triggers" section in http://dev.mysql.com/doc/refman/5.5/en/stored-program-restrictions.html you can read:
Triggers are not permitted on tables in the mysql database.
BTW, are you using the mysql schema to store data? This is (usually) a very bad idea and probably you need to rethink your setup.
Here I have the following trigger:
USE dbsspf;
DELIMITER $$
CREATE
DEFINER = 'root'#'localhost'
TRIGGER TR_ASSIGN_PAGEINDEX
AFTER INSERT
ON LIB_RECORDS
FOR EACH ROW
BEGIN
UPDATE LIB_RECORDS
SET
PAGE_INDEX = 13;
END
$$
DELIMITER ;
As you can see I'm just updating the table in my trigger. However when I insert a new record the trigger does not fire. Can you please show me what I've missed?
B.5.9: Can triggers access tables?
A trigger can access both old and new data in its own table. A trigger
can also affect other tables, but 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.
http://dev.mysql.com/doc/refman/5.5/en/faqs-triggers.html#qandaitem-B-5-1-9
In other words, a trigger on LIB_RECORDS cannot write other rows on LIB_RECORDS.