在 MySQL 中创建非 NULL 约束以使 fieldA 和 fieldB 不能都为 NULL 的最佳方法是什么。我不在乎任何一个本身是否为 NULL,只要另一个字段具有非 NULL 值即可。如果它们都具有非 NULL 值,那就更好了。
6 回答
这不是直接回答您的问题,而是一些附加信息。
在处理多个列并检查是否全部为空或一个不为空时,我通常使用COALESCE()
- 如果列表增长,它会简短、易读且易于维护:
COALESCE(a, b, c, d) IS NULL -- True if all are NULL
COALESCE(a, b, c, d) IS NOT NULL -- True if any one is not null
这可以在您的触发器中使用。
@Sklivvz:使用 MySQL 5.0.51a 进行测试,我发现它解析了 CHECK 约束,但没有强制执行。我可以插入 (NULL, NULL) 而没有错误。测试了 MyISAM 和 InnoDB。随后使用 SHOW CREATE TABLE 表明 CHECK 约束不在表定义中,即使我在定义表时没有给出错误。
这与MySQL 手册相匹配:“CHECK 子句已解析,但被所有存储引擎忽略。”
因此对于 MySQL,您必须使用触发器来强制执行此规则。唯一的问题是 MySQL 触发器无法引发错误或中止 INSERT 操作。您可以在触发器中导致错误的一件事是将 NOT NULL 列设置为 NULL。
CREATE TABLE foo (
FieldA INT,
FieldB INT,
FieldA_or_FieldB TINYINT NOT NULL;
);
DELIMITER //
CREATE TRIGGER FieldABNotNull BEFORE INSERT ON foo
FOR EACH ROW BEGIN
IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
SET NEW.FieldA_or_FieldB = NULL;
ELSE
SET NEW.FieldA_or_FieldB = 1;
END IF;
END//
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error
您还需要一个类似的触发器 BEFORE UPDATE。
MySQL 5.5 引入了SIGNAL,因此我们不再需要 Bill Karwin 答案中的额外列。Bill 指出您还需要一个更新触发器,所以我也将其包括在内。
CREATE TABLE foo (
FieldA INT,
FieldB INT
);
DELIMITER //
CREATE TRIGGER InsertFieldABNotNull BEFORE INSERT ON foo
FOR EACH ROW BEGIN
IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null';
END IF;
END//
CREATE TRIGGER UpdateFieldABNotNull BEFORE UPDATE ON foo
FOR EACH ROW BEGIN
IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null';
END IF;
END//
DELIMITER ;
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error
UPDATE foo SET FieldA = NULL; -- gives error
这是此类约束的标准语法,但 MySQL 之后很高兴地忽略了该约束
ALTER TABLE `generic`
ADD CONSTRAINT myConstraint
CHECK (
`FieldA` IS NOT NULL OR
`FieldB` IS NOT NULL
)
我在 SQL Server 中做过类似的事情,我不确定它是否可以直接在 MySQL 中工作,但是:
ALTER TABLE tableName ADD CONSTRAINT constraintName CHECK ( (fieldA IS NOT NULL) OR (fieldB IS NOT NULL) );
至少我相信这是语法。
但是,请记住,您不能跨表创建检查约束,您只能检查一个表中的列。
我使用带有COALESCE ... NOT NULL的GENERATED ALWAYS列来完成此操作:
DROP TABLE IF EXISTS `error`;
CREATE TABLE IF NOT EXISTS `error` (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
left_id BIGINT UNSIGNED NULL,
right_id BIGINT UNSIGNED NULL,
left_or_right_id BIGINT UNSIGNED GENERATED ALWAYS AS (COALESCE(left_id, right_id)) NOT NULL,
when_occurred TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
message_text LONGTEXT NOT NULL,
INDEX id_index (id),
INDEX when_occurred_index (when_occurred),
INDEX left_id_index (left_id),
INDEX right_id_index (right_id)
);
INSERT INTO `error` (left_id, right_id, message_text) VALUES (1, 1, 'Some random text.'); -- Ok.
INSERT INTO `error` (left_id, right_id, message_text) VALUES (null, 1, 'Some random text.'); -- Ok.
INSERT INTO `error` (left_id, right_id, message_text) VALUES (1, null, 'Some random text.'); -- Ok.
INSERT INTO `error` (left_id, right_id, message_text) VALUES (null, null, 'Some random text.'); -- ER_BAD_NULL_ERROR: Column 'left_or_right_id' cannot be null
在 MySQL 版本 8.0.22 上