【问题标题】:How do I add a custom CHECK constraint on a MySQL table?如何在 MySQL 表上添加自定义 CHECK 约束?
【发布时间】:2011-09-22 22:04:20
【问题描述】:

这张桌子有问题

CREATE TABLE `Participants` (
  `meetid` int(11) NOT NULL,
  `pid` varchar(15) NOT NULL,
  `status` char(1) DEFAULT NULL,
  PRIMARY KEY (`meetid`,`pid`),
  CONSTRAINT `participants_ibfk_1` FOREIGN KEY (`meetid`) REFERENCES `Meetings` (`meetid`) ON DELETE CASCADE
  CONSTRAINT `participants_ibfk_2` CHECK (status IN ('a','d','u'))
  CONSTRAINT `participants_ibfk_3` CHECK (pid IN (SELECT name FROM Rooms) OR pid IN (SELECT userid FROM People))
);

我想要一个外键约束,这很有效。然后,我还想为属性status 添加一个约束,所以它只能取值'a'、'd'和'u'。我无法将该字段设置为Enumset

谁能告诉我为什么这段代码在 MySQL 中不起作用?

【问题讨论】:

标签: mysql sql database constraints check-constraints


【解决方案1】:

CHECK 约束不受 MySQL 支持。你可以定义它们,但它们什么都不做(从 MySQL 5.7 开始)。

来自manual

CHECK 子句被解析,但被所有存储引擎忽略。

解决方法是创建triggers,但它们并不是最容易使用的东西。

如果您想要一个支持CHECK 约束的开源RDBMS,请尝试PostgreSQL。它实际上是一个非常好的数据库。

【讨论】:

  • 感谢您简短而具体的回答
  • 有时我想知道为什么还有人使用 MySQL ——“哎呀,抱歉,我们决定不实现数据完整性!”。如果您对 RDBMS 有任何形式的控制权,并且想要开源,那么 Postgres 就是最佳选择。
  • 如何避免主键出现负值?没有check(id>0) 功能。使用 mysql 5.5
  • MariaDB 10.2 实现了 CHECK 约束。
  • 这个答案不再适用于 MySQL 5.8.0.16 及更高版本。 dev.mysql.com/doc/refman/8.0/en/…
【解决方案2】:

我不明白为什么这里没有人提到 VIEW WITH CHECK OPTION 可以替代 MySQL 中的 CHECK CONSTRAINT

CREATE VIEW name_of_view AS SELECT * FROM your_table
WHERE <condition> WITH [LOCAL | CASCADED] CHECK OPTION;

MySQL 网站上有一个文档:The View WITH CHECK OPTION Clause

DROP TABLE `Participants`;

CREATE TABLE `Participants` (
  `meetid` int(11) NOT NULL,
  `pid` varchar(15) NOT NULL,
  `status` char(1) DEFAULT NULL check (status IN ('a','d','u')),
  PRIMARY KEY (`meetid`,`pid`)
);

-- should work
INSERT INTO `Participants` VALUES (1,1,'a');
-- should fail but doesn't because table check is not implemented in MySQL
INSERT INTO `Participants` VALUES (2,1,'x');

DROP VIEW vParticipants;
CREATE VIEW vParticipants AS 
  SELECT * FROM Participants WHERE status IN ('a','d','u')
  WITH CHECK OPTION;

-- should work
INSERT INTO vParticipants VALUES (3,1,'a');
-- will fail because view uses a WITH CHECK OPTION
INSERT INTO vParticipants VALUES (4,1,'x');

P.S.:请记住,您的视图应该是可更新的!见MySQL Updatable Views (感谢 Romeo Sierra 在 cmets 中的澄清)。

【讨论】:

  • 我不明白为什么这里没有人提到..”,因为这是一种解决方法。但是,当然,这以非常非常有效的方式解决了问题。您需要做的只是确保您的视图是可更新的,瞧!问题消失了。为此 +1。
【解决方案3】:

除了触发器之外,对于像您所拥有的那样简单的约束:

CONSTRAINT `participants_ibfk_2` 
  CHECK status IN ('a','d','u')

您可以使用 status 中的 Foreign Key 到引用表(ParticipantStatus 有 3 行:'a','d','u'):

CONSTRAINT ParticipantStatus_Participant_fk
  FOREIGN KEY (status)
    REFERENCES ParticipantStatus(status) 

【讨论】:

  • 感谢您的建议。
【解决方案4】:

从 8.0.16 版本开始,MySQL 增加了对 CHECK 约束的支持:

ALTER TABLE topic
ADD CONSTRAINT post_content_check
CHECK (
    CASE
        WHEN DTYPE = 'Post'
        THEN
            CASE
                WHEN content IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);
 
ALTER TABLE topic
ADD CONSTRAINT announcement_validUntil_check
CHECK (
    CASE
        WHEN DTYPE = 'Announcement'
        THEN
            CASE
                WHEN validUntil IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);

以前,这仅在使用 BEFORE INSERT 和 BEFORE UPDATE 触发器时可用:

CREATE
TRIGGER post_content_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;
 
CREATE
TRIGGER post_content_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;
 
CREATE
TRIGGER announcement_validUntil_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;
 
CREATE
TRIGGER announcement_validUntil_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;

【讨论】:

    【解决方案5】:

    这是一种快速轻松地获得所需支票的方法:

    drop database if exists gtest;
    
    create database if not exists gtest;
    use gtest;
    
    create table users (
      user_id       integer unsigned not null auto_increment primary key,
      username      varchar(32) not null default '',
      password      varchar(64) not null default '',
      unique key ix_username (username)
    ) Engine=InnoDB auto_increment 10001;
    
    create table owners (
      owner_id      integer unsigned not null auto_increment primary key,
      ownername     varchar(32) not null default '',
      unique key ix_ownername (ownername)
    ) Engine=InnoDB auto_increment 5001;
    
    create table users_and_owners (
      id    integer unsigned not null primary key,
      name  varchar(32) not null default '',
      unique key ix_name(name)
    ) Engine=InnoDB;
    
    create table p_status (
      a_status      char(1) not null primary key
    ) Engine=InnoDB;
    
    create table people (
      person_id integer unsigned not null auto_increment primary key,
      pid       integer unsigned not null,
      name      varchar(32) not null default '',
      status    char(1) not null,
      unique key ix_name (name),
      foreign key people_ibfk_001 (pid) references users_and_owners(id),
      foreign key people_ibfk_002 (status) references p_status (a_status)
    ) Engine=InnoDB;
    
    create or replace view vw_users_and_owners as
    select 
      user_id id,
      username name
    from users
    union
    select 
      owner_id id,
      ownername name
    from owners
    order by id asc
    ;
    
    create trigger newUser after insert on users for each row replace into users_and_owners select * from vw_users_and_owners;
    create trigger newOwner after insert on owners for each row replace into users_and_owners select * from vw_users_and_owners;
    
    insert into users ( username, password ) values
    ( 'fred Smith', password('fredSmith')),
    ( 'jack Sparrow', password('jackSparrow')),
    ( 'Jim Beam', password('JimBeam')),
    ( 'Ted Turner', password('TedTurner'))
    ;
    
    insert into owners ( ownername ) values ( 'Tom Jones'),( 'Elvis Presley'),('Wally Lewis'),('Ted Turner');
    
    insert into people (pid, name, status) values ( 5001, 'Tom Jones', 1),(10002,'jack Sparrow',1),(5002,'Elvis Presley',1);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-16
      • 2015-08-26
      • 1970-01-01
      • 2015-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多