为什么我的 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);
根据您的评论,event
和 artist
都是主键的一部分:
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
如果您想要 event
或 artist
必须是 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
,所以你的测试无论如何都不重要。
我想要一个条件,即作业中的艺术家或事件为空,但即使它有艺术家,它也会拒绝作业。
触发器:
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);
根据您的评论,event
和 artist
都是主键的一部分:
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
如果您想要 event
或 artist
必须是 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
,所以你的测试无论如何都不重要。