《MySQL系列-InnoDB引擎28》表-约束详细介绍

news2024/11/15 13:38:42

在这里插入图片描述

约束

1 数据完整性

  关系型数据库系统和文件系统的一个不同点是,关系数据库本身能保证存储数据的完整性,不需要应用程序的控制,而文件系统一般需要在程序端进行控制。当前几乎所有的关系型数据库都提供约束(constraint)机制,该机制提供了一条强大而简易的途径来保证数据库中数据的完整性。一般来说,数据完整性有以下三种形式:

  • **1.实体完整性**保证表中有一个主键。在InnoDB存储引擎表中,用户可以通过定义Primary Key和Unique Key约束来保证实体的完整性。用户还可以通过编写一个触发器来保证数据完整性。
  • **2.域完整性**保证数据每列的值满足特定的条件。在InnoDB存储引擎表中,域完整性可以通过以下几种途径保证。
    • 选择合适的数据类型确保一个数据值满足特定条件
    • 外键(Foreign Key)约束
    • 编写触发器
    • 还可以考虑用default约束作为强制域完整性的一个方面
  • **3.参照完整性**保证两张表之间的关系。InnoDB存储引擎支持外键,因此允许用户定义外键以强制参照完整性,也可以通过编写触发器以强制执行。

对于InnoDB引擎本身,提供了以下几种约束:

  • Primary Key
  • Unique Key
  • Foregin Key
  • Default
  • NOT NULL

2 约束的创建和查找

约束的创建可以采用以下两种方式:

  • 表建立时就进行约束定义
  • 利用ALTER TABLE命令进行创建约束

  对于Unique Key(唯一索引)的约束,用户还可以通过命令create unique index来建立。

Primary Key

## 1.创建表,id是Primary Key
mysql> create table constraint_test(
    -> id int,
    -> primary key (id)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.05 sec)

## 2.查询元数据信息可以发现
## Primary Key的约束名是Primary
mysql> select constraint_name,constraint_type
    -> from information_schema.table_constraints
    -> where table_schema='zxy' and table_name='constraint_test'\G;
*************************** 1. row ***************************
constraint_name: PRIMARY
constraint_type: PRIMARY KEY

Unique Key

  • 方式一:alter table...add unique key... 添加

    # 1.添加字段name
    mysql> alter table constraint_test add column name varchar(25);
    Query OK, 0 rows affected (0.08 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    # 2.对name添加unique key,并指定约束名为uk_name
    mysql> alter table constraint_test add unique key uk_name (name);
    Query OK, 0 rows affected (0.09 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    # 3.查询元数据
    mysql> select constraint_name,constraint_type
        -> from information_schema.table_constraints
        -> where table_schema='zxy' and table_name='constraint_test'\G;
    *************************** 1. row ***************************
    constraint_name: PRIMARY
    constraint_type: PRIMARY KEY
    *************************** 2. row ***************************
    constraint_name: uk_name
    constraint_type: UNIQUE
    
    
  • 方式二:create unique index ...添加

    mysql> alter table constraint_test add column sex varchar(25) ;
    Query OK, 0 rows affected (0.07 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> create unique index un_sex on constraint_test(sex);
    Query OK, 0 rows affected (0.04 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> select constraint_name,constraint_type
        -> from information_schema.table_constraints
        -> where table_schema='zxy' and table_name='constraint_test'\G;
    *************************** 1. row ***************************
    constraint_name: PRIMARY
    constraint_type: PRIMARY KEY
    *************************** 2. row ***************************
    constraint_name: uk_name
    constraint_type: UNIQUE
    *************************** 3. row ***************************
    constraint_name: un_sex
    constraint_type: UNIQUE
    3 rows in set (0.00 sec)
    

Foregin Key

## 1.创建外键表constraint_foreign
# id为primary key
# test_id为constraint_test的外键
mysql> create table constraint_foreign(
    -> id int ,
    -> test_id int ,
    -> name varchar(25) ,
    -> primary key (id),
    -> foreign key (test_id) references constraint_test(id)
    -> );

# 2.查看元数据信息
mysql> select constraint_name,constraint_type
    -> from information_schema.table_constraints
    -> where table_schema='zxy' and table_name='constraint_foreign'\G;
*************************** 1. row ***************************
constraint_name: PRIMARY
constraint_type: PRIMARY KEY
*************************** 2. row ***************************
constraint_name: constraint_foreign_ibfk_1
constraint_type: FOREIGN KEY
2 rows in set (0.00 sec)

# 3.用户还可以通过information_constraints查看表的外键属性
mysql> select *
    -> from information_schema.referential_constraints
    -> where constraint_schema='zxy' and table_name = 'constraint_foreign'\G;
*************************** 1. row ***************************
       CONSTRAINT_CATALOG: def
        CONSTRAINT_SCHEMA: zxy
          CONSTRAINT_NAME: constraint_foreign_ibfk_1
UNIQUE_CONSTRAINT_CATALOG: def
 UNIQUE_CONSTRAINT_SCHEMA: zxy
   UNIQUE_CONSTRAINT_NAME: PRIMARY
             MATCH_OPTION: NONE
              UPDATE_RULE: RESTRICT
              DELETE_RULE: RESTRICT
               TABLE_NAME: constraint_foreign
    REFERENCED_TABLE_NAME: constraint_test
1 row in set (0.00 sec)

3 约束和索引的区别

  我们可以看到在使用Primary Key和Unique Key的时候,就是正常创建索引的方法。那么约束和索引有什么区别呢?

  用户创建一个唯一索引就是创建了一个唯一的约束。但是约束和索引的概念有所不同,约束更像是一个逻辑的概念,用户保证数据的完整性,而索引是一个数据结构,既有逻辑上的概念,在数据库中还代表着物理存储的方式。

4 对错误数据的约束

  在某些默认设置下,MySQL数据库允许非法的或不正确的数据的插入或更新,又或者可以在数据库内部将其转化为一个合法的值。比如向NOT NULL的字段插入一个NULL值,MySQL数据库会将其更改为0再进行插入,因此数据库本身没有对数据的正确性进行约束。

  但是在插入非法的数据或不正确的数据的时候,会根据数据库的sql_mode来判断是报错还是警告。

# 1.查看sql_mode类型
mysql> select @@sql_mode\G;
*************************** 1. row ***************************
@@sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)

# 2.查看当前会话的sql_mode类型
mysql> select @@session.sql_mode\G;
*************************** 1. row ***************************
@@session.sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)

# 3.查看全局的sql_mode类型
mysql> select @@global.sql_mode\G;
*************************** 1. row ***************************
@@global.sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)

  此时设置有sql_mode,因此插入非法或不正确的数据会直接报错

mysql> create table constraint_null (
    -> id int not null,
    -> date date not null
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> insert into constraint_null select null,'2023-02-30';
ERROR 1048 (23000): Column 'id' cannot be null

  设置sql_mode为空,此时向NOT NULL的列插入了一个NULL值,同时向date列插入一个不合法的日期。这时候就没有报错信息,只是显示了警告(warning)。通过指令show warnings可以查看警告的信息。

mysql> set session sql_mode = '';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into constraint_null select null,'2023-02-30';
Query OK, 1 row affected, 2 warnings (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 2

mysql> show warnings\G;
*************************** 1. row ***************************
  Level: Warning
   Code: 1048
Message: Column 'id' cannot be null
*************************** 2. row ***************************
  Level: Warning
   Code: 1264
Message: Out of range value for column 'date' at row 1
2 rows in set (0.00 sec)

mysql> select * from constraint_null;
+----+------------+
| id | date       |
+----+------------+
|  0 | 0000-00-00 |
+----+------------+
1 row in set (0.00 sec)

5 ENUM和SET约束

  MySQL数据库不支持传统的CHECK约束,但是通过ENUM和SET类型可以解决部分这样的约束需求。例如表上有一个性别类型,规定域的范围只能是male或female,在这种情况下用户可以通过ENUM类型来进行约束。

mysql> create table enum_test (
    -> id int ,
    -> sex enum('male','female')
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into enum_test values(1,'male'),(2,'female');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into enum_test values(1,'test');
ERROR 1265 (01000): Data truncated for column 'sex' at row 1

  如上所示,对非法的输入值进行了约束,但是只限于对离散数值的约束,对于传统check约束支持的连续值的范围或更复杂的约束,ENUM和SET类型还是无能为力,这时用户需要通过触发器来实现对于值域的约束。

6 触发器与约束

  触发器的作用是在执行INSERT、UPDATE、DELETE命令前后自动调用SQL命令或存储过程。

  触发器的创建命令是CREATE TRIGGER,只有具备Super权限的MySQL数据库用户才可以执行这条命令。

CREATE
[DEFINER = {user | current_user}]
TRIGGER trigger_name DEFORE|AFTER INSERT|UPDATE|DELETE
ON table_name FOR EACH ROW 

  最多可以为一个表建立6个触发器,即分别是INSERT、UPDATE、DELETE的BEFORE和AFTER各定义一个。BEFORE和AFTER代表触发器发生的时间,表示在每行操作的之前还是之后发生。MySQL只支持行级触发器for each row,不支持语句级触发器for each statement

  通过触发器,用户可以实现MySQL数据库本身并不支持的一些特性,比如对于传统check约束的支持,物化视图、高级复制、审计等特性。这里先关注触发器对于约束的支持。

案例

  加入有张用户消费表,每次用户购买一样物品后其金额都是减的,若这时残生了负值的操作,这样用户的金额不减反增

mysql> create table usercash(
    -> userid int not null,
    -> cash int unsigned not null
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into usercash values(1,1000);
Query OK, 1 row affected (0.01 sec)

mysql> update usercash
    -> set cash=cash-(-20)
    -> where userid=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from usercash;
+--------+------+
| userid | cash |
+--------+------+
|      1 | 1020 |
+--------+------+
1 row in set (0.00 sec)

  如上所示,SQL语句对于数据库来说是没有任何问题的,可以正常的运行,不会报错。但是从业务逻辑上讲这是绝对错误的。所以可以通过触发器来约束这个行为:

# 1.创建存储错误业务数据表
mysql> create table usercash_err_log (
    -> userid int not null,
    -> old_cash int unsigned not null,
    -> new_cash int unsigned not null,
    -> user varchar(30),
    -> time datetime
    -> );
Query OK, 0 rows affected (0.02 sec)

# 2.创建触发器
mysql> delimiter &&
mysql> create trigger trigger_user_cash_updatebefore before update
    -> on usercash
    -> for each row
    -> begin
    -> if new.cash - old.cash > 0 then
    -> insert into usercash_err_log
    -> select old.userid,old.cash,new.cash,user(),now();
    -> set new.cash = old.cash;
    -> end if;
    -> end;
    -> &&
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;

# 3.业务数据负数情况
mysql> update usercash
    -> set cash = cash - (-20)
    -> where userid = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

# 4.查看错误业务数据是否影响业务
mysql> select * from usercash;
+--------+------+
| userid | cash |
+--------+------+
|      1 | 1020 |
+--------+------+
1 row in set (0.00 sec)

# 5.查看错误日志表
mysql> select * from usercash_err_log;
+--------+----------+----------+----------------+---------------------+
| userid | old_cash | new_cash | user           | time                |
+--------+----------+----------+----------------+---------------------+
|      1 |     1020 |     1040 | root@localhost | 2023-03-09 16:44:36 |
+--------+----------+----------+----------------+---------------------+
1 row in set (0.00 sec)

  可以看到对于异常的数据更新通过触发器将其保存到usercash_err_log。此外该触发器还记录了操作该SQL语句的用户及时间。通过上述例子可以发现,创建触发器也是实现约束的一种手段和方法。

7 外键约束

  外键用来保证参照完整性,MySQL数据库的MyISAM存储引擎本身并不支持外键,对于外键的定义只是起到一个注释的作用。而InnoDB存储引擎则完整支持外键约束。

[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (index_col_name,...)
REFERENCES tb1_name (index_col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
referece_optio:
RESTRICT | CASCADE |SET NULL|NO ACTION

  用户可以在执行CREATE TABLE时就添加外键,也可以在表创建后ALTER TABLE命令来添加。一个简单的外键的创建示例如下:

mysql> create table parent(
    -> id int not null,
    -> primary key (id)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.02 sec)

mysql> create table child(
    -> id int ,
    -> parent_id int,
    -> foreign key (parent_id) references parent(id)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.02 sec)

  一般来说,称被引用的表为父表,引用的表为子表。外键定义时的ON DELETE和ON UPDATE表示在对父表进行DELETE和UPDATE操作时,对子表所做的操作,可定义的子表操作有:

  • CASCADE

    CASCADE表示当父表发生DELETE或UPDATE操作时,对相应的子表中数据进行DELETE或UPDATE操作。

  • SET NULL

    SET NULL表示当父表发生DELETE或UPDATE操作时,相应的子表中的数据被更新为NULL值。

  • NO ACTION

    NO ACTION表示当父表发生DELETE或UPDATE操作时,抛出错误,不允许这类操作发生

  • RESTRICT

    RESTRICT表示当父表发生DELETE或UPDATE操作时,抛出错误,不允许这类操作发生。

  如果定义外键没有指定ON DELETE或ON UPDATE,RESTRICT就是默认的外键设置。

  在其他数据库中,如Oracle数据库,有一种称为延时检查(deferred check)的外键约束,即检查在SQL语句运行完成后再进行。而目前MySQL数据库的外键约束都是即时检查(immediate check),因此从上面的定义可以看出,在MySQL数据库中NO ACTION和RESTRICT功能都是相同的。

  在Oracle数据库中,对于建立外键的列,一定不要忘记给这个列加上一个索引。而InnoDB存储引擎在外键建立时会自动对该列加一个索引,这和SQL Server数据库做法一样。因此可以很好的避免外键列上无索引而导致死锁的问题。例如上述的例子,表child创建时只定义了外键,并没有手动指定parent_id列为索引,但是通过命令show create table可以发现InnoDB引擎自动为外键约束的列parent_id添加了索引:

mysql> show create table child\G;
*************************** 1. row ***************************
       Table: child
Create Table: CREATE TABLE `child` (
  `id` int(11) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  KEY `parent_id` (`parent_id`),
  CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

  对于参照完整性约束,外键能起到一个非常好的作用。但是对数据的导入操作时,外键往往导致在外键约束的检查上花费大量时间。因为MySQL数据库的外键是即时检查的,所以对导入的每一行都会进行外键检查。但是在导入过程中忽视外键的检查,如:


mysql> set foreign_key_checks = 0;
Query OK, 0 rows affected (0.00 sec)
...
加载数据
...
mysql> set foreign_key_checks = 1;
Query OK, 0 rows affected (0.00 sec)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

群智能优化计算中的混沌映射

经实验证明,采用混沌映射产生随机数的适应度函数值有明显提高,用混沌映射取代常规的均匀分布的随机数发生器可以得到更好的结果,特别是搜索空间中有许多局部解时,更容易搜索到全局最优解,利用混沌序列进行种群初始化、…

基于Qt WebEngine 的Web仪器面板GUI程控技术

随着IIoT的发展,很多工业仪器也具备了远程管理的GUI。与早期使用串口进行命令交互不同,这些GUI可以直接在远程呈现数据。 作为希望对仪器、软件进行二次开发的小公司来说,会遇到GUI人工操作转自动化的需求。在无法通过串口等传统接口进行自动…

nextjs开发 + vercel 部署 ssr ssg

前言 最近想实践下ssr 就打算用nextjs 做一个人博客 , vercel 部署 提供免费域名,来学习实践下ssr ssg nextjs 一个轻量级的react服务端渲染框架 vercel 由 Next.js 的创建者制作 支持nextjs 部署 免费静态网站托管 初始化项目 npx create-next-app p…

【Linux】目录结构

Linux世界里,一切皆文件。 /bin:是Binary的缩写,这个目录存放着最经常使用的命令。(常用) /sbin:s就是Super User的意思,这里存放的是系统管理员使用的系统管理程序。 /home:存放普…

关于Pytorch中的张量学习

关于Pytorch中的张量学习 张量的概念和创建 张量的概念 Tensor是pytorch中非常重要且常见的数据结构,相较于numpy数组,Tensor能加载到GPU中,从而有效地利用GPU进行加速计算。但是普通的Tensor对于构建神经网络还远远不够,我们需…

实力加持!RestCloud完成多方国产化适配,携手共建信创生态

近年来,随着数字化建设进入深水区,企事业单位对信息安全重视程度与日俱增,核心技术自主可控已成为时代呼唤,国产化浪潮日益汹涌澎湃。近日,RestCloud在国产化方面取得新进展,完成了全部产品线信创环境的多方…

系统重装漏洞

zzcms系统重装漏洞 一、配置zzcms环境 1. 使用小皮搭建zzcms框架 2. 安装zzcms 按照下面的操作进行,傻瓜式操作即可 3. 打开网站 二、漏洞利用 在访问install目录的默认文件后,会出现zzcms安装向导 http://www.zzcms.com/install/index.php 但是会显示 “安装向导…

MQTT协议-发布消息(客户端向服务器发送)

MQTT协议-发布消息(客户端向服务器发送) 发布消息报文组成:https://blog.csdn.net/weixin_46251230/article/details/129414158 在分析完服务器下发到客户端的报文后,就可以参考JSON格式的有效载荷,将温湿度的值改为…

Linux发行版的backport

遇到一个问题,简要记录如下: base on ubuntu18.06 4.15内核,这版内核不支持一款intel的集成网卡,追踪内核代码的提交历史才发现,这款网卡是从linux-4.20才开始支持的,系统自带的这个Kernel版本不支持。 如果不允许升级内核,面对这种问题,社区的做法是把新内核的特性cher…

顺序表【数据结构】

文章目录:star2:1. 顺序表概念:star2:2. 框架3. 基本功能3.1 头文件:star:3.2 初始化:star:3.3 扩容:star:3.4 打印:star:3.5 尾插:star:3.6 头插:star:3.7 尾删:star:3.8 头删:star:3.9 指定插入:star:3.10 指定删除:star:3.11 查找:star2:3.12 注意事项4. 顺序表的缺点&#…

云原生安全2.X 进化论系列|云原生安全2.X未来展望(4)

随着云计算技术的蓬勃发展,传统上云实践中的应用升级缓慢、架构臃肿、无法快速迭代等“痛点”日益明显。能够有效解决这些“痛点”的云原生技术正蓬勃发展,成为赋能业务创新的重要推动力,并已经应用到企业核心业务。然而,云原生技…

Git学习笔记(六)-标签管理

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签…

销售使用CRM系统集成Excel的五个技巧

销售过程中有很多情况会降低团队的效率。通过正确的实施CRM客户管理系统,可以帮助您的企业自动执行手动任务、减少错误并专注于完成交易。这里有5个技巧,可以帮助您的销售人员通过CRM集成Excel为销售流程赋能并提高他们的整体效率。 技巧1:将…

Python每日一练(20230309)

目录 1. 删除有序数组中的重复项 ★ 2. 二叉树的最小深度 ★★ 3. 只出现一次的数字 II ★★ 🌟 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 1. 删除有序数组中的重复项 给你一个有序数组 nums ,请你原地删除重复出现的元素…

Xuetr杀毒工具使用实验(28)

实验目的 (1)学习Xuetr的基本功能; (2)掌握Xuetr的基本使用方法。预备知识 windows操作系统的基本知识如:进程、网络、服务和文件等的了解。 XueTr是近年推出的一款广受好评的ARK工具。ARK工具全称为Anti R…

Ubuntu20.04中Docker安装与配置

一、安装 1、卸载可能存在的旧版本 sudo apt-get remove docker docker-engine docker-ce docker.io2、更新apt包索引 sudo apt-get update显示“正在读取软件包列表… 完成” 3、安装以下包以使apt可以通过HTTPS使用存储库(repository) sudo apt-get install -y apt-tran…

java多线程(二三)并发编程:Callable、Future和FutureTask

一、Callable 与 Runnable 先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法: public interface Runnable {public abstract void run(); }由于run()方法返回值为void类型,所以在执行完任务之后无法返…

关于React Hook(18)

useState():👉详情 (必须“有条件地调用”;注意避免冗余状态的产生) 关于useState的两种使用方式的区别:👉详情 关于batch机制:有条件地调用一些状态的set方…

L3-021 神坛

在古老的迈瑞城,巍然屹立着 n 块神石。长老们商议,选取 3 块神石围成一个神坛。因为神坛的能量强度与它的面积成反比,因此神坛的面积越小越好。特殊地,如果有两块神石坐标相同,或者三块神石共线,神坛的面积…

STM32F103R8T6 SPWM实现正弦波输出

前言 PWM合成正弦波,原理什么的不详细说了,概括一下就是 PWM有效面积的积分 正弦波的有效面积。PWM的频率越快,细分的越多,锯齿也就越不明显。 做法是:首先利用正弦波取点软件,取点1000个,生…