12

在 MySQL 中创建非 NULL 约束以使 fieldA 和 fieldB 不能都为 NULL 的最佳方法是什么。我不在乎任何一个本身是否为 NULL,只要另一个字段具有非 NULL 值即可。如果它们都具有非 NULL 值,那就更好了。

4

6 回答 6

8

这不是直接回答您的问题,而是一些附加信息。

在处理多个列并检查是否全部为空或一个不为空时,我通常使用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

这可以在您的触发器中使用。

于 2008-10-05T00:12:41.577 回答
7

@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。

于 2008-10-04T17:35:47.277 回答
5

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
于 2015-02-27T22:39:29.470 回答
4

这是此类约束的标准语法,但 MySQL 之后很高兴地忽略了该约束

ALTER TABLE `generic` 
ADD CONSTRAINT myConstraint 
CHECK (
  `FieldA` IS NOT NULL OR 
  `FieldB` IS NOT NULL
) 
于 2008-10-04T15:38:51.880 回答
2

我在 SQL Server 中做过类似的事情,我不确定它是否可以直接在 MySQL 中工作,但是:

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

至少我相信这是语法。

但是,请记住,您不能跨表创建检查约束,您只能检查一个表中的列。

于 2008-10-04T15:31:24.453 回答
0

我使用带有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 上

于 2020-12-21T15:14:32.477 回答