Question

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;
Was it helpful?

Solution

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

OTHER TIPS

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.

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; 

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;

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
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top