Either OR non-null constraints in MySQL

前端 未结 6 1532
忘了有多久
忘了有多久 2021-02-05 23:06

What\'s the best way to create a non-NULL constraint in MySQL such that fieldA and fieldB can\'t both be NULL. I don\'t care if either one is NULL by itself, just as long as the

相关标签:
6条回答
  • 2021-02-05 23:12

    MySQL 5.5 introduced SIGNAL, so we don't need the extra column in Bill Karwin's answer any more. 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
    
    0 讨论(0)
  • 2021-02-05 23:15

    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(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.

    0 讨论(0)
  • 2021-02-05 23:19

    This is the standard syntax for such a constraint, but MySQL blissfully ignores the constraint afterwards

    ALTER TABLE `generic` 
    ADD CONSTRAINT myConstraint 
    CHECK (
      `FieldA` IS NOT NULL OR 
      `FieldB` IS NOT NULL
    ) 
    
    0 讨论(0)
  • 2021-02-05 23:23

    I accomplished this using a GENERATED ALWAYS column with COALESCE ... NOT NULL:

    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

    0 讨论(0)
  • 2021-02-05 23:25

    @Sklivvz: Testing with MySQL 5.0.51a, I find it parses a CHECK constraint, but does not enforce it. I can insert (NULL, NULL) with no error. Tested both MyISAM and 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.

    This matches the MySQL manual which says: "The CHECK clause is parsed but ignored by all storage engines."

    So for MySQL, you would have to use a trigger to enforce this rule. The only problem is that MySQL triggers have no way of raising an error or aborting an INSERT operation. One thing you can do in the trigger to cause an error is to set a NOT NULL column to 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.

    0 讨论(0)
  • 2021-02-05 23:25

    I've done something similar in SQL Server, I'm not sure if it will work directly in MySQL, but:

    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.

    0 讨论(0)
提交回复
热议问题