Jobu
Jobu

Reputation: 631

NULL Handling in MySQL After Update Trigger that Fires Only on Data Change

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

Answers (2)

user2428118
user2428118

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

Andomar
Andomar

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

Related Questions