after update user I get this error
ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG: Can't update table 'users' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
And this is my trigger, I want to remove user when his fame under 0
CREATE TRIGGER check_fame AFTER UPDATE ON users FOR EACH ROW
BEGIN
IF NEW.fame < 0 THEN
DELETE FROM users WHERE login = OLD.login;
END IF;
END
What is the error ?
If you want fame to always be positive, then use a check constraint:
ALTER TABLE users ADD CONSTRAINT chk_users_fame CHECK (fame >= 0);
A trigger is not needed for this operation in the more recent versions of MySQL.
In older versions you can use a trigger but you want to prevent the update:
CREATE TRIGGER check_fame BEFORE UPDATE ON users FOR EACH ROW
BEGIN
IF NEW.fame < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Fame must never be negative';
END IF;
END;
Related
I have a table named Client , I want to update the available credit column after updating the balance field.
I created this trigger
DELIMITER $$
CREATE TRIGGER client_update_balance
AFTER UPDATE ON client
FOR EACH ROW
BEGIN
update client
set AvailableCredit = CreditLimit - new.Balance;
END
But when I update a balance value like that
SET SQL_SAFE_UPDATES = 0;
UPDATE client
SET
balance = 1500
WHERE
client.ClientNum = 143;
I get this error:
Error Code: 1442. Can't update table 'client' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
Checked some StackOverflow answers and tried without Update statement inside
DELIMITER $$
CREATE TRIGGER client_update_balance
AFTER UPDATE ON client
FOR EACH ROW
BEGIN
set New.AvailableCredit = New.CreditLimit - New.Balance;
END;$$
DELIMITER ;
I get another error
Error Code: 1362. Updating of NEW row is not allowed in after trigger
Use a before update trigger and just set the value:
DELIMITER $$
CREATE TRIGGER client_update_balance
BEFORE UPDATE ON client
FOR EACH ROW
BEGIN
set new.AvailableCredit = new.CreditLimit - new.Balance;
END;
The idea is that you want to change values in the same row of the table being updated. You don't need a separate update transaction to do that. You just need to adjust the values.
That said, if AvailableCredit is always defined as this difference, you should use a generated column. That way, you don't have to update the value. It is just correct when you query the table.
I am trying to get the quantity field synchronized in 2 tables on 2 different databases using triggers.
I found some examples and tried to adapt them to this requirement why not success and showing this error...
#1442 - Can't update table 'oc_product' in stored function/trigger because it is already used by statement which invoked this stored
function/trigger.
... when trying to perfom this query:
UPDATE `db`.`oc_product` SET `quantity` = '220' WHERE `oc_product`.`product_id` = 50
Trigger for db2, table stock:
DROP TRIGGER IF EXISTS t1_ai &&
CREATE TRIGGER t1_ai BEFORE UPDATE ON stock FOR EACH ROW BEGIN
IF #__disable_trigger_t1t2 = 1 THEN
SET #__disable_trigger_t1t2 = NULL;
ELSE
SET #__disable_trigger_t1t2 = 1;
-- trigger logic goes in here
UPDATE `db1`.oc_product
SET `quantity` = NEW.quantity
WHERE SKU = NEW.SKU;
END IF;
END &&
Trigger for db1, table oc_product:
DROP TRIGGER IF EXISTS t2_ai &&
CREATE TRIGGER t2_ai BEFORE UPDATE ON oc_product FOR EACH ROW BEGIN
IF #__disable_trigger_t1t2 = 1 THEN
SET #__disable_trigger_t1t2 = NULL;
ELSE
SET #__disable_trigger_t1t2 = 1;
-- trigger logic goes in here
UPDATE `db2`.stock
SET `quantity` = NEW.quantity
WHERE SKU = NEW.SKU;
END IF;
END &&
Any idea how to fix that error?
Extra information:
If I perfom this query for the stock table on db2:
UPDATE `db2`.`stock` SET `quantity` = '220' WHERE `stock`.`id` = 7631;
Then the trigger t1_ai do the job, but not vice-versa
Cause: You cannot update/delete/insert to a table (stock) where the trigger is invoked:
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.
Doing so will generate Error 1442:
Error Code: 1442
Can't update table 'oc_product' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
What is happening here, UPDATE on stock table fires the first trigger 't1_ai' which in it's SQL attempts to update the oc_product table, which has a trigger 't1_ai' which attempts an update/change back on the stock table.
So, as mentioned above you:
it is not permitted to modify a table that is already being used (for
reading or writing)
For this whole flow (cascading triggers) the stock is still in use (hence locked for further read or write) i.e. the first table update where these cascading triggers got fired.
Suggestion:
Try implementing a procedure and call it via your application.
HOW to create SQL statement that UPDATE record in the table and after updated check whether
value is not negative if yes, throw ERROR!
Since I use TRANSACTION (ROLLBACK,COMMIT) I will use it to define whether should
I commit or rollback.
Thank in advance.
You can create a trigger that checks the value after each insert. It will fire an error if the column value is less than zero.
delimiter |
CREATE TRIGGER `check_value` AFTER UPDATE ON your_table
FOR EACH ROW BEGIN
IF NEW.value < 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'value can not be negative';
END IF
END
|
delimiter ;
delimiter //
create trigger T1 before update on account
for each row
if NEW.balance <= 0 then
update account set balance=OLD.balance;
end if;
//
Query OK, 0 rows affected (0.09 sec)
delimiter ;
update account set balance=-1 where id=101;
ERROR 1442 (HY000): Can't update table 'account' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
MySQL uses row level triggers, so the trigger is fired for each row that is being changed. Because of that there is no reason to update the target table, just assign the value:
create trigger T1 before update on account
for each row
if NEW.balance <= 0 then
set new.balance = OLD.balance; -- this is the difference
end if;
SQLFiddle example: http://sqlfiddle.com/#!2/34aebb/1
Your update wouldn't have worked anyway because it was missing a where clause which means you would have updated the entire table just because a single row was changed.
I have a row in a table that I do not want to be changed (ever).
Is it possible to set a MySQL row to READ-ONLY so that it cannot be updated in any way? If so, how?
If not, is it possible to set a permanent value in one of the columns of that row so that it cannot be changed? If so, how?
Thanks.
This is likely to be business logic, which probably doesn't belong in your data storage layer. However, it can nonetheless be accomplished using triggers.
You can create a BEFORE UPDATE trigger that raises an error if a "locked" record is about to be updated; since an error occurs before the operation is undertaken, MySQL ceases to proceed with it. If you also want to prevent the record from being deleted, you'd need to create a similar trigger BEFORE DELETE.
To determine whether a record is "locked", you could create a boolean locked column:
ALTER TABLE my_table ADD COLUMN locked BOOLEAN NOT NULL DEFAULT FALSE;
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
UPDATE my_table SET locked = TRUE WHERE ...;
Note that SIGNAL was introduced in MySQL 5.5. In earlier versions, you must perform some erroneous action that causes MySQL to raise an error: I often call an non-existent procedure, e.g. with CALL raise_error;
I cannot create an additional column on this table, but the row has a unique id in one of the columns, so how would I do this for that scenario?
Again, if you absolutely must place this logic in the storage layer—and cannot identify the locked records through any means other than the PK—you could hard-code the test into your trigger; for example, to "lock" the record with id_column = 1234:
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
But this is absolutely horrible and I would do almost anything to avoid it whenever possible.