Reputation: 631
Due to reputation constraints, this is a follow-up question to this answer to a prior question. I'd like to know if there's a more efficient way to test each field for changes while handling NULL values.
CREATE TABLE foo (
a INT NULL DEFAULT NULL,
b INT NULL DEFAULT NULL,
c INT NULL DEFAULT NULL
);
CREATE TABLE bar (
a INT NULL DEFAULT NULL,
b INT NULL DEFAULT NULL,
c INT NULL DEFAULT NULL
);
INSERT INTO foo (a, b) VALUES (1, 2);
I want a trigger whose actions occur after update only if a value is changed by the update. So this UPDATE won't result in an INSERT:
UPDATE foo SET a = 1 WHERE b = 2;
But this UPDATE will result in an INSERT:
UPDATE foo SET a = 2 WHERE b = 2;
I have written this trigger to handle that, however I'm hoping the IF statement can be simplified.
DELIMITER ///
CREATE TRIGGER t_after_update_foo
AFTER UPDATE ON foo
FOR EACH ROW
BEGIN
IF
((OLD.a <> NEW.a OR OLD.a IS NULL OR NEW.a IS NULL) AND (NEW.a IS NOT NULL OR OLD.a IS NOT NULL))
OR
((OLD.b <> NEW.b OR OLD.b IS NULL OR NEW.b IS NULL) AND (NEW.b IS NOT NULL OR OLD.b IS NOT NULL))
OR
((OLD.c <> NEW.c OR OLD.c IS NULL OR NEW.c IS NULL) AND (NEW.c IS NOT NULL OR OLD.c IS NOT NULL))
THEN
INSERT INTO bar (a, b, c) VALUES (NEW.a, NEW.b, NEW.c);
END IF;
END;
///
DELIMITER ;
Can the IF statement be simplified? Or is there an easier overall solution?
Upvotes: 3
Views: 3643
Reputation: 8114
You can do this by comparing using the NULL-safe equals operator <=>
and then negating the result using NOT
.
So,
((OLD.a <> NEW.a OR OLD.a IS NULL OR NEW.a IS NULL) AND (NEW.a IS NOT NULL OR OLD.a IS NOT NULL))
would become
!(OLD.a <=> NEW.a)
To check if any of multiple columns have changed, you could do
!(OLD.a <=> NEW.a AND OLD.b <=> NEW.b)
A slightly shorter alternative if you have many colums to compare, courtesy of @dlauzon:
!((OLD.a, OLD.b, OLD.c, ...) <=> (NEW.a, NEW.b, NEW.c, ...))
Upvotes: 7
Reputation: 238206
You could use coalesce()
, which returns the first of its arguments that is not null
.
if coalesce(old.a,'') <> coalesce(new.a,'') or
coalesce(old.b,'') <> coalesce(new.b,'') or
coalesce(old.c,'') <> coalesce(new.c,'')
then
insert ...;
end if;
It can be tricky to choose the second argument. The above example works for the common case when a, b and c are strings and when an empty string value is equivalent to a null
value.
Upvotes: 5