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.
Related
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;
I have table "Months" with 12 rows(obviously) and I want to add a trigger that prevent adding new row if ID number is over 12
CREATE TRIGGER "no_more_months" BEFORE INSERT ON "months"
FOR EACH ROW BEGIN
IF NEW.ID_Month>12 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint on Months failed';
END IF;
END;
But somehow it doesn't work and still adds new rows
As said in the comment section, just a constraint should do the job.
ALTER TABLE [dbo].[Months]
ADD CONSTRAINT CK_Months_ID_Months CHECK ([ID_Months] <= 12 AND [ID_Months] > 0);
If you absolutely need a custom error message look at this article : MySql Signals
I'm using MySQL. I have a record on A table with a soft delete column: active with a value of 0.
This row is linked to 11 tables. All have the same active column.
I need to be sure that the record on A is deleted only if all the references across the 11 tables have active = 0 also.
I know I can write a view with these queries to get if I can "deleted" or not. But this is one example and IMO not very practical solution. Cascade update won't work either because I can't delete the parent row if any of the child is still active.
Thanks!
This should work if you create the active_view as you said you could. Just add the active flags of all the related tables into the foreign_active column, and you should be good to go.
CREATE TRIGGER before_update_student
BEFORE UPDATE ON student FOR EACH ROW
BEGIN
IF NEW.active = 0 AND (SELECT foreign_active FROM active_view
WHERE id = NEW.id) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete student when active roles exist.';
END IF;
END;
Is there a way to check if the rows already exist and is active?
Let say the rows look like that:
ID: 123456
Active : 1
I know I could do something like that:
SELECT * FROM TableName WHERE id=123456 AND Active=1;
But that's not what I want.
And if I try to insert a query into the table again with the same information it should not duplicate.
But the constraint should work if it's not active (0).
I don't want to do the verification code wise. I would like the database server to do the verification.
Thanks!
You could use a trigger before insert to block the insert if and only if there is already an active row.
create trigger tr_bi_TableName
before insert on TableName
for each row
begin
if((select count(*) from TableName where id = new.id and active = 1) > 0) then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'There is already an active row for this id.';
end if
end
SQLState 45000 means unhandled user-defined exception.
See documentaton for more info on signal.
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 ;