为什么我的 SQL 模拟 XOR 的触发器不起作用?

Why does my SQL trigger that simulates an XOR not work?

我想要一个条件,即作业中的艺术家或事件为空,但即使它有艺术家,它也会拒绝作业。

触发器:

delimiter $$
CREATE TRIGGER assignment_event_or_artist BEFORE INSERT ON assignment
FOR EACH ROW 
BEGIN 
    IF(
        (NEW.event IS NULL AND NEW.artist IS NULL) || 
        (NEW.event IS NOT NULL AND NEW.artist IS NOT NULL)
    )
    THEN
        SIGNAL SQLSTATE '44000'
            SET MESSAGE_TEXT = 'new assignment does not have an event xor an artist';
    END IF;
END$$
delimiter ;

作业:

INSERT INTO assignment(edition, festival, artist, category) VALUES ("2021", "Le Guess Who?", "Bent Arcana", "SOLL");

错误代码:

ERROR 1644 (44000) at line 487: new assignment does not have an event xor an artist

我做错了什么?

编辑:正如 solarflare 在他的回答中指出的那样,作业的 table 定义很重要,因此我将其包含在此处:

CREATE TABLE assignment ( edition YEAR NOT NULL, festival VARCHAR(200) NOT NULL, event INT, artist VARCHAR(200), category VARCHAR(200), PRIMARY KEY (edition, festival, event, artist);

根据您的评论,eventartist 都是主键的一部分:

CREATE TABLE assignment ( 
     edition YEAR NOT NULL, 
     festival VARCHAR(200) NOT NULL, 
     event INT, 
     artist VARCHAR(200), 
     category VARCHAR(200), 
     PRIMARY KEY (edition, festival, event, artist), ... )

主键不能包含 null 值,如果不提供默认值,通常会收到错误消息

Field 'event' doesn't have a default value

如果您想要 eventartist 必须是 null 的情况,您需要修复您的主键。

要解释为什么您没有收到该错误,而是您的信号消息,有一些可能的原因:

  • 如果您不启用 strict sql mode,MySQL 会用隐式默认值替换缺失值(在这种情况下,它将 null 替换为 0,所以你的触发器中的两列都不为空)

  • 即使您设置了严格模式,MySQL up to 5.6 也会对未指定的主键列使用隐式默认值(与您正在使用的 table 定义匹配) :

Exception: If the column is defined as part of a PRIMARY KEY but not explicitly as NOT NULL, MySQL creates it as a NOT NULL column (because PRIMARY KEY columns must be NOT NULL), but also assigns it a DEFAULT clause using the implicit default value.

  • 如果您使用的是 MariaDB,这可能是未修复的 MariaDB bug 的副作用(这是由 null 在内部替换为 0 的类似行为引起的) .

因此,通过这些(或类似)效果之一,在触发器内部,event 已被赋予值 0,因此两列都是 not null,从而满足您的 if条件。您实际上可以在该 if 语句中测试 0

但再次强调:潜在的问题是主键列不能是null,所以你的测试无论如何都不重要。