8

I want to make a column in a relation unmodifyable for consistency reasons.

The backstory is that I have a n:n relationship where the "connecting" relation has additional values. I don't want that anyone is able to change the IDs of the relationship partners.

What I have come up with so far:

Creating a trigger that checks if the NEW value is the same as the OLD value. But I don't know how to deny the UPDATE action if the condition proves as TRUE.

Here is my trigger so far:

CREATE TRIGGER deny_nton_change BEFORE UPDATE ON Schueler_in_Klasse
FOR EACH ROW BEGIN
    IF NEW.Schueler_ID != OLD.Schueler_ID OR NEW.Klasse_ID != OLD.Klasse_ID THEN

    END IF;
END;

5 Answers 5

12

Rather than abort the update, just override it?

...
FOR EACH ROW BEGIN
    SET NEW.Schueler_ID = OLD.Schueler_ID;
    SET NEW.Klasse_ID = OLD.Klasse_ID;
END
2
  • +1 because this is much simpler than my approach. I am going to look over the MySQL Stored Procedure Language Book (amazon.com/dp/0596100892) and see it suggests this in the same chapter. Nov 24, 2011 at 18:22
  • BTW congrats on 20000 rep and being the first DBA.SE trusted user. Nov 28, 2011 at 17:10
4

A fairly nice and clean way to abort and return a readable error message is by using a signal:

SIGNAL sqlstate '45000' SET message_text = 'Schueler_ID and Klasse_ID may not be altered!';

Do make sure your MySQL version supports this, it was introduced in MySQL 5.5. In earlier versions you need hacks like inserting a non existant field into a non existant table.

As gbn mentions, overwriting the new values with the old ones is a good option too.

2
  • As suggested by @RolandoMySQLDBA, this seems to not work... It says there is an error in my SQL syntax
    – sinni800
    Nov 29, 2011 at 11:06
  • like I said, check the mysql version. It works on 5.5 fine. link To signal a generic SQLSTATE value, use '45000', which means “unhandled user-defined exception.”
    – Jannes
    Dec 1, 2011 at 11:37
2

I actually learned a back-alley approach to aborting triggers. It is very crude, but works because SIGNAL processing is not implemented in MySQL's Stored Procedure Language. It can be emulated at best.

Chapter 11, Pages 254-256 of the book MySQL Stored Procedure Programming under the subheading 'Validating Data with Triggers' suggests making a SELECT of a character string into a dummy integer (in terms of syntax, it is correct, but dies on execution [NO PUN INTENDED]):

CREATE TRIGGER deny_nton_change BEFORE UPDATE ON Schueler_in_Klasse 
FOR EACH ROW BEGIN
    DECLARE dummy INT; 
    IF NEW.Schueler_ID != OLD.Schueler_ID OR NEW.Klasse_ID != OLD.Klasse_ID THEN 
        SELECT 'Cannot Carry Out This Trigger Any Further'
        INTO dummy FROM table_you_know_does_not_exist WHERE 1=1 LIMIT 1;
    END IF; 
END; 
1
  • Wow, that is really... Back-alley. Nice one...
    – sinni800
    Nov 24, 2011 at 18:20
1

It is actually possible to do this neatly without using triggers if you are using InnoDB.

Create another table with just one column. That column should have a foreign key (hence the innodb requirement in this solution) that points to the immutable column of the original table in question.

Put a restriction like "ON UPDATE RESTRICT".

In summary:

CREATE TABLE original (
     ....
     immutable_column ...
     INDEX index1(immutable_column)
     ....
) ENGINE=INNODB;

CREATE TABLE restricter (
     .....
     col1,
     INDEX index2(col1),
     FOREIGN KEY (col1) REFERENCES original (immutable_colum) ON UPDATE RESTRICT ON DELETE CASCADE
) ENGINE=INNODB;
3
  • 1
    This looks smart but I see a problem. The column may not be unique (and probably isn't in this case) so a foreign key towards it, is not advised although allowed in MySQL. Jul 13, 2014 at 11:36
  • 1
    +1 though because if you use the (Schueler_ID, Klasse_ID) combination instead of the one column, it should work. Jul 13, 2014 at 11:39
  • This should still work even when immutable_column is not unique. Aug 2, 2017 at 9:29
0

Based on a combination of all answers I have read this is what worked for me:

delimiter $$ -- You may need this or not (in my visual client I need it, directly in console I don't)
create trigger deny_nton_change before update on surveys
for each row begin
    if new.Schueler_ID != old.Schueler_ID OR new.Klasse_ID != old.Klasse_ID then
      signal sqlstate '45000' set message_text = 'My own Error: Field is immutable';
    end if;
end$$
delimiter ; -- You may need this or not

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.