MySql triggers can't update same table - mysql

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 ;

Related

Error Code: 1442. Can't update table 'customer' in stored function/trigger because it is already used by statement which invoked this stored function

I'm writing a query that creates a trigger to soft delete a row in the table customer using the a flag called "IsDelete" when the flag is '0' it is not deleted and when the flag becomes 1 the row has been marked as deleted.
When the query is run the error code 1442c is generated. can anyone explain why??
DELIMITER $$
CREATE TRIGGER SOFT_DELETE_TRIGGER
BEFORE DELETE ON customer
FOR EACH ROW
BEGIN
IF OLD.IsDelete = 0 THEN
CALL cannot_delete_error;
UPDATE customer
SET IsDelete = 1;
END IF;
END
$$
Deleting a row in the table to test the trigger.
DELETE FROM customer
WHERE C_username = 'testuser'
Yes, triggers don't allow you to INSERT/UPDATE/DELETE against the same table that spawned the trigger, because that could run invoke the trigger again, and then that trigger might do another INSERT/UPDATE/DELETE, and so on. You have too high a chance of causing an infinite loop.
I'm afraid MySQL doesn't support an "instead of" trigger like some other brands of SQL database do. You can't make a trigger on DELETE that does an update instead.
You can use SIGNAL to make it throw an error, and that blocks the DELETE, but it doesn't do an UPDATE instead.
To implement soft deletes as you are doing, you'll just have to make the client use UPDATE instead of DELETE.

Alter a where clause on a update through a trigger or similar

I'm currently building a replication database (for reporting from) for an already existing database and we are wanting to obfuscate/hash certain columns. Both are on AWS RDS platform with the replication set up as a 'read replica' of the source.
One of the issues with using RDS Replication I've found is you cannot specify which columns to ignore (given that RDS read replicas are supposed to be 1:1 this isn't surprising and I fully understand I'm doing something very niche)
The solution to this problem was to set up triggers on updates/inserts to alter these values, like so:
DELIMITER $$
CREATE TRIGGER clients_insert_obfuscate
BEFORE INSERT ON clients
FOR EACH ROW
BEGIN
SET NEW.access_token = NULL, NEW.user_token_ttl = 0;
END$$
DELIMITER ;
However one of the values we want to alter is a primary key (the PKs on this table are used as coupon code redemption numbers).
Which leads me to my question - is there anyway of altering the where clause in a update before its executed from within mysql? So on the replica databse, instead of matching against the unhashed code its matching against the hashed code?
So far I have this:
DELIMITER $$
CREATE TRIGGER insert_obfuscate
BEFORE INSERT ON codes
FOR EACH ROW
BEGIN
SET NEW.code = SHA2(NEW.code, 256);
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER update_obfuscate
BEFORE UPDATE ON codes
FOR EACH ROW
BEGIN
SET NEW.code = SHA2(NEW.code, 256);
END$$
DELIMITER ;
So the insert is fine - but the update trigger is only looking at the params to update - not the where clause.
I understand a trigger might not be the right route to take on this but I'm struggling to find anything else.
Is there anyway of doing this or do I need to look at changing the schema? I would prefer not to change the schema on a production DB though.
Thanks

How to create a trigger for before/after delete with update/insert in mysql?

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.

mysql stored procedure glitching

I have to limit table record to 25. after I just delete everything (in the future will modify it to delete just oldest rows)
DELIMITER //
CREATE PROCEDURE p1 () delete FROM tests;//
CREATE TRIGGER trigger1
BEFORE INSERT
ON tests
FOR EACH ROW
BEGIN
SELECT COUNT(*) INTO #cnt FROM tests;
IF #cnt >= 25 THEN
CALL p1();
END IF;
END
//
DELIMITER ;
, but I am getting error:
Can't update table 'tests' in stored function/trigger because it is already used by statement which invoked this stored function/trigger
So I can not add any more fields.
The MySQL trigger FAQ sais that you cannot modify the table that calls the trigger.
But you can set up a cron job, or CREATE EVENT in MySQL that cleans the table at regular intervals. (CREATE EVENT needs the PROCESS privilege, and a running event_scheduler. The event_scheduler is turned off by default: it can be turned on from SQL console, but the MySQL config must be modified to ensure that it starts when MySQL restarts.)
It seems that you can't update or delete from the same table which invoked the trigger.
But you can work around this by creating another table, for example tests2 and instead of inserting into tests just insert into tests2, and have the trigger insert the NEW. values into tests, then you can count from tests and delete from tests like how you want it.
see this sqlFiddle
The problem with this is then tests2 gets filled up with Inserted data So you might have to manually delete from tests2.

MySQL: trigger with clause "instead of update"

I'm trying to create a database with history in mind (experience shows you'll have to do this one day or another).
I've asked here database-design-how-to-handle-the-archive-problem but there's no better anser than the link here.
My problem is about where to do the code and technically, how (MySQL gives me headaches). First I've started doing this in Php: before doing any insert, duplicate the record mark it as "obsolete" then modify the record.
But there's a dependency problem (manytomany and manytoone associations must be updated as well) which implies coding (one way or another) all the dependancies and updates that come with the tables (which is not acceptable).
So I'm thinking about doing all the work on the database server side. This would greatly simplify my Php code.
The problem is that I have to "archive" the current record before modifying it. To do so, the code must be in a trigger "before update".
Here's my code:
DELIMITER ;;
DROP TRIGGER IF EXISTS produit_trigger_update_before;
CREATE TRIGGER produit_trigger_update_before
BEFORE UPDATE ON produit
FOR EACH ROW BEGIN
/* */
INSERT INTO produit SET
id_origine = OLD.id_origine,
date_v_creation = OLD.date_v_creation,
date_v_start = OLD.date_v_debut,
date_v_end = NOW(),
...
last_record = OLD.last_record;
/* Dependancies : */
SET #last=LAST_INSERT_ID();
UPDATE categorie_produit SET id_produit=#last
WHERE id_produit = OLD.id;
UPDATE produit_attribut SET id_produit=#last
WHERE id_produit = OLD.id;
END;;
DELIMITER ;;
If I get this code working, all my problems are gone. But damn it, it's not working:
mysql> update produit set importance=3;
ERROR 1442 (HY000): Can't update table 'produit' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
mysql> Bye
In this page there's a working sample, which uses INSTEAD OF UPDATE clause in the trigger. MySQL doesn't seem to support this.
So my question is both conceptual (= have you any other "principle" that could work) and/or technical (= can you make this trigger work).
If I get this code working, all my problems are gone. But damn it, it's not working:
As a rule you can't have a trigger on table A trigger inserts into table A - since that could cause an endless loop. (Trigger mutation in Oracle terms)
Personally I would not do this using triggers. Triggers can do "audit logging" - but this is not what you want here.
I suggest you solve it programatically - either with a PHP function or a MySQL stored procedure (whatever your preference) that you call something like "ModifyProduit".
The code would then do basically what you have the trigger above do. (It might be easier to just have the code set date_v_end on the current row, and then insert a completly new row. That way you don't have to mess around with updating your referenced tables)
you can do history of a table with an auxiliary table like this (i've done this for many tables on mysql and the speed is very good):
table produit_history has the same structure as produit + 2 additional columns: "history_start DATETIME NOT NULL" and "history_stop DATETIME DEFAULT NULL".
there are 3 triggers on produit table:
AFTER INSERT: in this trigger there is a simple insert into produit_history of the same data with history_start = NOW() and history_stop = NULL (NULL means the current row is valid)
AFTER UPDATE: this trigger performs two queries. The first is un update like this:
UPDATE produit_history set history_stop = NOW() WHERE id_origine = OLD.id_origine AND history_stop IS NULL;
The second query is an insert identical to the one in the AFTER INSERT trigger.
AFTER DELETE: this triggers there is a simple update which is identical to the one in the AFTER UPDATE.
You can then query this history table and obtain snapshots at whatever time you're interested in with the following where condition:
WHERE (history_start &lt= "interesting_time" AND (history_stop IS NULL OR history_stop &gt "interesting_time"))