有趣的特性:CHECK约束

功能说明

在MySQL 8.0.16以前, CREATE TABLE允许从语法层面输入下列CHECK约束,但实际没有效果:

CHECK (expr)

在 MySQL 8.0.16,CREATE TABLE添加了针对所有存储引擎的表和列的CHECK约束的核心特性。CREATE TABLE允许如下针对表或列的约束语法:

[CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]
  • 可选的symbol指定了约束的名称,如果省略,MySQL会自动生成一个类似:${table_name}_check_${seq_num}的约束名称,约束名称是大小写敏感的,且最长可以到64个字符
  • expr设定了一个返回值为boolean类型的约束条件,表达式对所有的数据行评估的结果值为:TRUEUNKNOWN(对 NULL值),当值为FALSE时,约束就被违反,产生的效果与执行的语句有关

  • 可选的执行子句标识约束是否需要被强制:

    • 当未指定或指定为: ENFORCED时,约束被创建且生效

    • 当指定为: NOT ENFORCED时,约束被创建但未生效

  • 一个CHECK约束可以被指定为表约束或列约束

    • 表约束不会出现在列定义内,可以引用任意多个或一个列,且允许引用后续定义的表列

    • 列约束出现在列定义内,仅允许引用该列

示例如下:

CREATE TABLE t1
(
CHECK (c1 <> c2),
c1 INT CHECK (c1 > 10),
c2 INT CONSTRAINT c2_positive CHECK (c2 > 0),
c3 INT CHECK (c3 < 100),
CONSTRAINT c1_nonzero CHECK (c1 <> 0),
CHECK (c1 > c3)
);

以上示例包含了列约束和表约束,命名和未命名的格式:

  • 第一个约束是一个不包含在任何列定义内的表约束,所以允许引用任意列,且引用了后续定义的列,同时没有给出约束名称,所以MySQL会给该约束生成一个名字

  • 后续的3个约束是包含在列定义内的列约束,所有指定引用所在的列

  • 最后的两个是表约束

如果想查看上述命令所生成的约束名,可以输入以下SHOW CREATE TABLE命令:

mysql> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) DEFAULT NULL,
`c2` int(11) DEFAULT NULL,
`c3` int(11) DEFAULT NULL,
CONSTRAINT `c1_nonzero` CHECK ((`c1` <> 0)),
CONSTRAINT `c2_positive` CHECK ((`c2` > 0)),
CONSTRAINT `t1_chk_1` CHECK ((`c1` <> `c2`)),
CONSTRAINT `t1_chk_2` CHECK ((`c1` > 10)),
CONSTRAINT `t1_chk_3` CHECK ((`c3` < 100)),
CONSTRAINT `t1_chk_4` CHECK ((`c1` > `c3`))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

SQL规范要求:所有约束(包括:PRIMARY KEY, UNIQUEFOREIGN KEY, CHECK)属于同一个命名空间(NAMESPACE),在MySQL实现中,所有的约束类型在每个schema (database)内有自己的命名空间。所以,CHECK约束的名称在SCHEMA内必须唯一,也就是说不允许有两张表使用同一个CHECK约束名称。(例外:一个临时表可能使用与非临时表一样的约束名称)

CHECK的条件表达式必须遵守以下规则,如果包含不允许的结构,将会触发错误:

  • 非生成列和生成列允许被添加到表达式,但包含AUTO_INCREMENT属性的列和其他表的列不允许被加入

  • 字面量和确定性(deterministic)的内置函数以及操作符允许被添加到表达式,确定性的含义是:同样的数据不同用户的多次调用的结果是一致的,非确定性的函数包括:CONNECTION_ID()CURRENT_USER()NOW()

  • 存储函数和用户自定义函数不被允许

  • 存储过程不被允许

  • 变量:系统变量、用户自定义变量和存储过程的本地变量均不被允许使用

  • 子查询不应许被使用

  • 外键参考动作,如:ON UPDATEON DELETE被禁止在包含CHECK约束的列使用,相应的,CHECK约束也被禁止在使用外键参考动作的列使用

  • CHECK约束在插入、更新、替换(REPLACE)和LOAD DATA/XML语句的时候被评估,如果评估结果是FALSE将触发错误,如果错误发生,已经提交的数据的处理与对应存储引擎是否支持事务有关,也依赖严格SQL模式是否生效

  • 如果约束表达式所需的数据类型与声明的列类型不一致,数据将参考MySQL的类型转换规则被隐式的转换

约束表达式在不同的SQL模式下,可能返回不同的结果

另外,在INFORMATION_SCHEMACHECK_CONSTRAINTS表中存放着所有表中定义的CHECK约束的信息。

建议使用CHECK约束的场景

复杂业务场景下的约束,从架构角度看,允许有不同的实现方式:

  • 放在数据库表中,通过约束实现,但不支持子查询

  • 放在数据库中,通过触发器(TRIGGER)实现

  • 放在应用程序的逻辑中,在提前数据库前检查

一般性的,选择不同方式的原则如下:

  • 如果CHECK约束可以实现,且约束比较稳定,一般用CHECK约束实现,比如:年龄不允许为负数,不允许>150等,比如:
CREATE TABLE Departments (
ID int NOT NULL,
PID int NOT NULL,
Name varchar(255) NOT NULL Default '',
CHECK (ID>=1)
);
-- add check separately
ALTER TABLE Departments
ADD CONSTRAINT CHK_PID CHECK (ID>=1 AND PID >=0);
-- remove check
ALTER TABLE Departments
DROP CHECK CHK_PID;
  • 如果属于数据库逻辑,比如:审计,外键可以使用触发器
CREATE TABLE IF NOT EXISTS `department` (
`id` int NOT NULL AUTO_INCREMENT,
`pid` int COMMENT 'parent id',
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB; CREATE TRIGGER pid_insert_check
BEFORE INSERT ON department
FOR EACH ROW
BEGIN
IF (NEW.pid <> 0 AND NEW.pid NOT IN (select id from department)) THEN
signal sqlstate '45000'
set message_text = 'department parent id has to be chosen from id';
END IF;
END CREATE TRIGGER pid_delete_check
BEFORE DELETE ON department
FOR EACH ROW
BEGIN
IF (OLD.id < 0 OR OLD.id IN (select pid from department)) THEN
signal sqlstate '45000'
set message_text = 'department parent id has to be chosen from id';
END IF;
END
  • 如果属于业务逻辑,建议放在应用层处理,方便开发者:理解和维护,但是:也需要通过强化业务管理,避免特权用户偶发操作引起对数据完整性的破坏

Enjoy GreatSQL

文章推荐:

面向金融级应用的GreatSQL正式开源

Changes in GreatSQL 8.0.25 (2021-8-18)

MGR及GreatSQL资源汇总

GreatSQL MGR FAQ

在Linux下源码编译安装GreatSQL/MySQL

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

Gitee:

https://gitee.com/GreatSQL/GreatSQL

GitHub:

https://github.com/GreatSQL/GreatSQL

Bilibili:

https://space.bilibili.com/1363850082/favlist

微信&QQ群:

QQ群:533341697

微信群:可搜索添加GreatSQL社区助手微信好友,发送验证信息“加群”加入GreatSQL/MGR交流微信群

GreatSQL社区助手:wanlidbc

有趣的特性:CHECK约束的更多相关文章

  1. MariaDB10.2.X-新特性2-支持check约束and with as

    前几天写了一篇MariaDB10.2支持分析函数,大家印象中MySQL不支持with as ,check约束,那么MariaDB10.2也同样给你惊喜 1.with as MariaDB [test1 ...

  2. SQL Server中使用Check约束提升性能

        在SQL Server中,SQL语句的执行是依赖查询优化器生成的执行计划,而执行计划的好坏直接关乎执行性能.     在查询优化器生成执行计划过程中,需要参考元数据来尽可能生成高效的执行计划, ...

  3. SQL PRIMARY KEY 约束\SQL FOREIGN KEY 约束\SQL CHECK 约束

    SQL PRIMARY KEY 约束 PRIMARY KEY 约束唯一标识数据库表中的每条记录. 主键必须包含唯一的值. 主键列不能包含 NULL 值. 每个表都应该有一个主键,并且每个表只能有一个主 ...

  4. oracle之check约束小结

    一下是Ocp考试指导中,对于oracle约束的描述: The constraint types supported by the Oracle database are as follows:UNIQ ...

  5. oracle 序列 ,check约束

    ====================序列 //查询当前用户序列 select * from user_sequences //查询所有序列 select * from all_sequences; ...

  6. Oracle之Check约束实例具体解释

    Oracle | PL/SQL Check约束使用方法具体解释 1. 目标 实例解说在Oracle中怎样使用CHECK约束(创建.启用.禁用和删除) 2. 什么是Check约束? CHECK约束指在表 ...

  7. MySQL关于check约束无效的解决办法

    首先看下面这段MySQL的操作,我新建了一个含有a和b的表,其中a用check约束必须大于0,然而我插入了一条(-2,1,1)的数据,其中a=-2,也是成功插入的. 所以MySQL只是check,但是 ...

  8. SQLServer之修改CHECK约束

    使用SSMS数据库管理工具修改CHECK约束 1.打开数据库,选择数据表->右键点击->选择设计(或者展开约束,选择约束,右键点击,选择修改,后面步骤相同). 2.选择要修改的数据列-&g ...

  9. SQLServer之CHECK约束

    CHECK约束添加规则 1.CHECK 约束用于限制列中的值的范围. 2.Check约束通过逻辑表达式来判断数据的有效性,用来限制输入一列或多列的值的范围,在列中更新数据时,所要输入的内容必须满足Ch ...

随机推荐

  1. 给小白的 PG 容器化部署教程(下)

    作者:王志斌 编辑:钟华龙 本文来自社区小伙伴 王志斌 的投稿.从小白的角度,带你一步步实现将 RadonDB PostgreSQL 集群部署到 Kubernetes 上.文章分为上下两部分,< ...

  2. pycharm编辑器下载与安装

    pycharm编辑器下载与安装 首先是下载地址:https://www.jetbrains.com/pycharm/download/#section=windows   首先根据自己的电脑选择系统, ...

  3. 计算机环境变量的配置,以java为例以及eclipse简要设置

    安装JDK时可以不安装公共jre.因为好多软件和浏览器已经默认自带的jre了,或者自动调用系统的了. 在java 中需要设置三个环境变量(1.5之后不需要再设置CLASSPATH了,但需要的话可以设置 ...

  4. .Net CLR GC动态获取函数头地址,C++的骚操作(慎入)

    前言: 太懒了,从没有在这里正儿八经的写过文章.看到一些人的高产,真是惭愧.决定稍微变得不那么懒.如有疏漏,请指正. .net的GC都谈的很多了,本篇主要是剑走偏锋,聊聊一些个人认为较为核心的细节方面 ...

  5. tensorflow版本的bert模型 GPU的占用率为100%而其利用率为0%

    Notice: 本方法只是解决问题的一种可能,不一定百分百适用,出现这个问题还有很多其他原因,这个可以作为解决的一种尝试!!! 经过检查发现,是由于激活环境的原因 使用 conda activate ...

  6. 想写个小说,关于C#的,名字就叫《原Csharp》吧 (第一回 买书未成炁自生 惶惶回屋遇老翁)

    以前也有写过一些小说,但是总是写写停停的,因为忙于项目和其他事情,总是耽搁很久(真的是很久)才会继续动两笔,所以我想先在这里以随笔的方式写个关于C#异世界的小故事吧,更新随缘,也稍微能让自己轻松些. ...

  7. Python基础学习笔记_01

    Python的介绍 1989年圣诞节创造,1991年正真出生,目前更新到3.0版本 具有最庞大的"代码库",人称"胶水语言",无所不能 一种跨平台的计算机程序设 ...

  8. Vscode标题栏出现【不受支持】提示的完美解决方案

    安装Fix VSCode Checksums code --install-extension lehni.vscode-fix-checksums 打开命令面板,快捷键 Ctrl + Shift + ...

  9. Linux 安装Apche服务

    用yum 进行在线安装apche服务 yum install -y httpd 我这边是centos7 需要开启一下端口: 1 firewall-cmd --zone=public --add-por ...

  10. 解决方案:可以ping别人,但是别人不能ping我

    背景:我在写分布式爬虫项目时遇到了slave端无法ping通我的master,我的master可以ping通slave.我将master的防火墙关闭后slave可以ping了,但是这不是解决办法.于是 ...