MySQL 中的 CHECK 约束不起作用

Posted

技术标签:

【中文标题】MySQL 中的 CHECK 约束不起作用【英文标题】:CHECK constraint in MySQL is not working 【发布时间】:2011-01-08 02:24:03 【问题描述】:

首先我创建了一个类似的表

CREATE TABLE Customer (
  SD integer CHECK (SD > 0),
  Last_Name varchar (30),
  First_Name varchar(30)
);

然后在该表中插入值

INSERT INTO Customer values ('-2','abc','zz');

mysql 没有显示错误,它接受了这些值。

【问题讨论】:

部分同意。鉴于您尝试使用它,可以假设您同时问了这两个问题。其实你接受的答案主要是解释为什么不行。 您可以对此功能请求投票:bugs.mysql.com/bug.php?id=3464,但十年来没有受到任何关注。 您可以在 MariaDB 中使用来自version 10.2.1 的 CHECK 约束。 现在 MySQL 8.0.16 终于解决了这个问题,是时候默默地认识到(!)也忽略了内联 REFERENCES 规范(另一个大的 SQL 标准不兼容):bugs.mysql.com/bug.php?id=102904。 在 MySQL 8.x 中存在 CHECK CONSTRAINT 支持 【参考方案1】:

CHECK 约束似乎没有在 MySQL 中实现。

查看此错误报告:https://bugs.mysql.com/bug.php?id=3464

【讨论】:

已在 MariaDB 中修复(请参阅此答案 ***.com/a/44333349)。【参考方案2】:

MySQL 8.0.16 是第一个支持 CHECK 约束的版本。

阅读https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html

如果您使用 MySQL 8.0.15 或更早版本,MySQL Reference Manual 表示:

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

试试触发器...

mysql> delimiter //
mysql> CREATE TRIGGER trig_sd_check BEFORE INSERT ON Customer 
    -> FOR EACH ROW 
    -> BEGIN 
    -> IF NEW.SD<0 THEN 
    -> SET NEW.SD=0; 
    -> END IF; 
    -> END
    -> //
mysql> delimiter ;

希望对您有所帮助。

【讨论】:

这里您将了解如何触发错误:***.com/a/7189396/1144966 这是我将始终使用 PostgreSQL 而不是 MySQL 的众多原因之一。 我想知道如果解析器遇到定义的CHECK 约束,是否会在 MySQL 中开发 10 分钟或 15 分钟来引发警告。啊,那太简单了……【参考方案3】:

试试set sql_mode = 'STRICT_TRANS_TABLES'SET sql_mode='STRICT_ALL_TABLES'

【讨论】:

这实际上没有帮助(MySQL 5.6)它可以防止输入错误类型的数据,但不能输入不符合CHECK约束的数据【参考方案4】:

CHECK 约束被 MySQL 忽略,如文档中的小评论中所述:CREATE TABLE

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

【讨论】:

@thefiloe: 正确,在正确实现CHECK 约束的其他DBMS 中,如果CHECK 评估为FALSE,则插入(或更新)未完成并导致错误. 已在 MariaDB 中修复(请参阅此答案 ***.com/a/44333349)。 @Jérôme 我知道,我有一些(更新的)答案,其中包括该领域的改进(在 MariaDB 正确实施 CHECK 约束之前,在 MariaDB 和 MySQL 中都有其他解决此问题的方法)。我不确定我是否应该去编辑我所有的旧答案! 我想我的评论带有一个更新的答案的链接是好的。或者总比没有好。也许我应该编辑。我并不是要强迫你做任何事情。【参考方案5】:

不幸的是 MySQL 不支持 SQL 检查约束。出于兼容性原因,您可以在 DDL 查询中定义它们,但它们会被忽略。

有一个简单的替代方案

您可以创建BEFORE INSERTBEFORE UPDATE 触发器,当不满足数据要求时,它们会导致错误或将字段设置为其默认值。

BEFORE INSERT 在 MySQL 5.5 之后工作的示例

DELIMITER $$
CREATE TRIGGER `test_before_insert` BEFORE INSERT ON `Test`
FOR EACH ROW
BEGIN
    IF CHAR_LENGTH( NEW.ID ) < 4 THEN
        SIGNAL SQLSTATE '12345'
            SET MESSAGE_TEXT := 'check constraint on Test.ID failed';
    END IF;
END$$   
DELIMITER ;  

在 MySQL 5.5 之前,您必须导致错误,例如调用未定义的过程。

在这两种情况下,这都会导致隐式事务回滚。 MySQL 不允许 ROLLBACK 语句本身在过程和触发器中。

如果您不想回滚事务(即使“检查约束”失败,INSERT / UPDATE 也应该通过,您可以使用 SET NEW.ID = NULL 覆盖该值,这会将 id 设置为字段默认值,不会对于一个身份来说真的很有意义

编辑: 删除了杂散引用。

关于:= 运算符:

= 不同,:= 运算符永远不会被解释为比较运算符。这意味着您可以在任何有效的 SQL 语句(不仅仅是在 SET 语句中)使用:= 为变量赋值。

https://dev.mysql.com/doc/refman/5.6/en/assignment-operators.html

关于反引号标识符引号:

标识符引号字符是反引号(“`”)

如果启用了 ANSI_QUOTES SQL 模式,也允许在双引号内引用标识符

http://dev.mysql.com/doc/refman/5.6/en/identifiers.html

【讨论】:

...不是很简单,至少与 CHECK 相比 :(。Coupla tutes: net.tutsplus.com/tutorials/databases/…, sitepoint.com/how-to-create-mysql-triggers 呃,这看起来很笨重。我想我宁愿在 python 中创建一个元组并在那里检查值而不是把它放进去。 快速提问:为什么不设置 DELIMITER 就行不通? 但是为什么呢?我的意思是,他们可能只是删除了该命令。保留命令并删除其功能有什么意义? 该命令是在 Mysql 遵循的 SQL 的 ANSI 标准中定义的。 dev.mysql.com/doc/refman/8.0/en/compatibility.html【参考方案6】:

正如 joanq MariaDB 所提到的,现在似乎支持 CHECK 约束以及其他好东西:

“支持检查约束 (MDEV-7563)。”

https://mariadb.com/kb/en/mariadb/mariadb-1021-release-notes/

【讨论】:

【参考方案7】:

从 8.0.15 版开始支持检查约束(尚未发布)

https://bugs.mysql.com/bug.php?id=3464

[1 月 23 日 16:24] 保罗·杜布瓦

开发者发布:已在 8.0.15 中修复。

以前,MySQL 允许有限形式的 CHECK 约束语法, 但解析并忽略了它。 MySQL 现在实现了 表和列 CHECK 约束,适用于所有存储引擎。 约束是使用 CREATE TABLE 和 ALTER TABLE 语句定义的。

【讨论】:

【参考方案8】:

更新到 MySQL 8.0.16 以使用checks

从 MySQL 8.0.16 开始,CREATE TABLE 允许 table 的核心功能 和列 CHECK 约束,适用于所有存储引擎。创建表 允许以下 CHECK 约束语法,对于两个表 约束和列约束

MySQL Checks Documentation

【讨论】:

以上是关于MySQL 中的 CHECK 约束不起作用的主要内容,如果未能解决你的问题,请参考以下文章

mysql的check约束问题···

为啥默认约束在mysql中不起作用? [复制]

如何修改或删除mysql CHECK约束 constraint

vue中的计算属性不起作用

MySQL添加约束

UITableViewCell 中的约束不起作用