简体   繁体   English

MySQL 中的 OR 非空约束

[英]Either OR non-null constraints in MySQL

What's the best way to create a non-NULL constraint in MySQL such that fieldA and fieldB can't both be NULL.在 MySQL 中创建非 NULL 约束以使 fieldA 和 fieldB 不能都为 NULL 的最佳方法是什么。 I don't care if either one is NULL by itself, just as long as the other field has a non-NULL value.我不在乎任何一个本身是否为 NULL,只要另一个字段具有非 NULL 值即可。 And if they both have non-NULL values, then it's even better.如果它们都具有非 NULL 值,那就更好了。

This isn't an answer directly to your question, but some additional information.这不是直接回答您的问题,而是一些附加信息。

When dealing with multiple columns and checking if all are null or one is not null, I typically use COALESCE() - it's brief, readable and easily maintainable if the list grows:当处理多列并检查所有列是否为空或一个不为空时,我通常使用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

This can be used in your trigger.这可以在您的触发器中使用。

@Sklivvz: Testing with MySQL 5.0.51a, I find it parses a CHECK constraint, but does not enforce it. @Sklivvz:使用 MySQL 5.0.51a 进行测试,我发现它解析了 CHECK 约束,但没有强制执行。 I can insert (NULL, NULL) with no error.我可以插入 (NULL, NULL) 没有错误。 Tested both MyISAM and InnoDB.测试了 MyISAM 和 InnoDB。 Subsequently using SHOW CREATE TABLE shows that a CHECK constraint is not in the table definition, even though no error was given when I defined the table.随后使用 SHOW CREATE TABLE 显示 CHECK 约束不在表定义中,即使我在定义表时没有给出错误。

This matches the MySQL manual which says: "The CHECK clause is parsed but ignored by all storage engines."这与MySQL 手册相符,它说:“CHECK 子句被解析但被所有存储引擎忽略。”

So for MySQL, you would have to use a trigger to enforce this rule.因此,对于 MySQL,您必须使用触发器来强制执行此规则。 The only problem is that MySQL triggers have no way of raising an error or aborting an INSERT operation.唯一的问题是 MySQL 触发器无法引发错误或中止 INSERT 操作。 One thing you can do in the trigger to cause an error is to set a NOT NULL column to NULL.您可以在触发器中执行的导致错误的一件事是将 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

You also need a similar trigger BEFORE UPDATE.您还需要一个类似的触发器 BEFORE UPDATE。

MySQL 5.5 introduced SIGNAL , so we don't need the extra column in Bill Karwin's answer any more. MySQL 5.5 引入了SIGNAL ,因此我们不再需要 Bill Karwin 的回答中的额外列。 Bill pointed out you also need a trigger for update so I've included that too.比尔指出你还需要一个更新触发器,所以我也包含了它。

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

This is the standard syntax for such a constraint, but MySQL blissfully ignores the constraint afterwards这是这种约束的标准语法,但 MySQL 之后很高兴地忽略了该约束

ALTER TABLE `generic` 
ADD CONSTRAINT myConstraint 
CHECK (
  `FieldA` IS NOT NULL OR 
  `FieldB` IS NOT NULL
) 

I've done something similar in SQL Server, I'm not sure if it will work directly in MySQL, but:我在 SQL Server 中做了类似的事情,我不确定它是否可以直接在 MySQL 中工作,但是:

ALTER TABLE tableName ADD CONSTRAINT constraintName CHECK ( (fieldA IS NOT NULL) OR (fieldB IS NOT NULL) );

At least I believe that's the syntax.至少我相信这是语法。

However, keep in mind that you cannot create check constraints across tables, you can only check the columns within one table.但是,请记住,您不能跨表创建检查约束,您只能检查一个表中的列。

I accomplished this using a GENERATED ALWAYS column with COALESCE ... NOT NULL :我使用带有COALESCE ... NOT NULLGENERATED 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

on MySQL version 8.0.22在 MySQL 版本 8.0.22 上

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM