数据库(Database,DB)是按照数据结构来组织、存储和管理数据的仓库,其本身可被看作电子化的文件柜,用户可以对文件中的数据进行增删改查等操作。
数据库系统是指在计算机系统中引入数据库后的系统,除了数据库,还包括数据库管理系统(Database Management System,DBMS)、数据库应用程序。
人工管理阶段
文件系统阶段
数据库系统阶段
美国国家标准协会(American National Standards Institute,ANSI)所属的标准计划与需求委员会(Standards Planning and Requirements Committee,SPARC)在1971年公布的研究报告中提出了 ANSI-SPARC体系结构,即三级模式结构(或三层体系结构)。
ANSI-SPARC最终没有成为正式标准,但它仍然是理解数据库管理系统的基础。
三级模式是指数据库管理系统从三个层次来管理数据,分别是外部层(External Level)、概念层(Conceptual Level)和内部层(Internal Level)。
这三个层次分别对应三种不同的模式,分别是外模式、概念模式和内模式。
在外模式与概念模式之间,以及概念模式与内模式之间,还存在映像,即二级映像。
外模式面向应用程序,描述用户的数据视图;内模式(又称物理模式、存储模式)面向物理上的数据库,描述数据在磁盘中如何存储;概念模式(又称为模式、逻辑模式)面向数据库设计人员,描述数据的整体逻辑结构。
由于三级模式比较抽象,为了好理解,假设有一个电子表格来保存商品信息表,如下:
概念模式类似于表格的列标题,他描述了商品表中包含哪些信息。
概念模式在数据库中描述的信息还有很多,如多张表之间的联系,表中每一列的数据类型和长度等。
内模式
Excel表格另存为文件时,可以选择保存的文件路径、保存类型等,这些与存储想关的描述信息相当于内模式。
在数据库中内模式描述数据的物理结构和存储方式,如堆文件、索引文件、散列(Hash)文件等。
外模式
在打开一个电子表格会显示表格中全部的数据,将数据提供给用户时,出于权限,安全控制等因素考虑,只允许用户看到一部分数据,或不同用户看到不同的数据,这样的需求就可以用视图来实现。视图和基本表的关系如下:
基本表中的数据是实际储存在数据库中的,而视图中的数据是查询或计算出来的。由此可见,外模式可以为不同的用户需求创建不同的视图,由于不同的用户需求不同,数据显示方式也会多种多样。
一个数据库中会有多个外模式,而概念模式和内模式则只有一个
三级模式是数据的三个抽象级别,每个级别关系的重点不同。为了使三级模式之间产生关联,数据库管理系统在三级模式之间提供了二级映像功能。
二级映像是一种规则,它规定了映像双方如何进行转换。通过二级映像,体现了逻辑和物理两个层面的数据独立性。
逻辑独立性
外模式/概念模式映像体现了逻辑独立性。逻辑独立性是指当修改了概念模式,不影响上一层的外模式。
例如,将表库存和销量拆分到另一张表中,此时概念模式发生了更改,但可以通过改变外模式/概念模式的映像,继续为用户提供原有视图。
由此可见,逻辑独立性能够让使用视图的用户感觉不到基本表的改变。
物理独立性
概念模式/内模式映像体现了物理独立性。物理独立性是指修改了内模式,不影响上层的概念模式和外模式。
例如,在 Excel 中将 .xls 文件另存为 .xlsx 文件,虽然文件格式变了,但打开文件后显示的表格内容一般不会发生改变。
在数据中,更换了更先进的存储结构,或者创建索引来加快查询速度,内模式会发生改变。此时,只需要改变概念模式/内模式映像,就不会影响到原有的概念模式。
物理独立性使得用户不必了解数据库内部的存储原理,即可正常使用数据库来保存数据。数据库管理系统会自动将用户的操作转换成物理级数据库的操作。
数据库系统涉及一些人员,主要包括数据库管理员(Database Administrator,DBA)、应用程序员(Application Programmer)和最终用户(End User)。
数据库管理员
数据库管理员负责管理和维护数据库,参与数据库的设计、测试和部暑。
应用程序员
应用程序员负责为最终用户设计和编写程序,并进行调试和安装,以便最终用户利用应用程序来对数据库进行存取操作。
最终用户
最终用户一般为非计算机专业人员,通过应用程序访问数据库。
数据建模是对现实世界中的各类数据的抽象组织,以确定数据库的管辖范围、数据的组织形式等。
数据建模大致分为3个阶段,分别是概念建模阶段、逻辑建模阶段、物理建模阶段,相应的产物分别是概念模型、逻辑模型和物理模型。
概念模型是现实世界到机器世界的中间层,它将现实世界中的客观对象(如学生、班级、课程)抽象成信息世界的数据。
逻辑模型是指数据的逻辑结构,可以选择层次模型、网状模型或关系模型。
在完成逻辑模型后,最后使用物理模型描述数据如何进行实际存储,也就是将逻辑模型转换成计算机能够识别的模型。
在概念模型中可以通过一些术语来对现实世界进行抽象。
实体
实体(Entity)是指客观存在并可相互区分的事物,如学生、班级、课程。
属性
属性(Attribute)是指实体所具有的某一特性,一个实体可以由若干个属性来描述。
例如,学生实体的属性有学号、学生姓名和学生性别。属性由两部分组成,分别是属性名和属性值。
联系
联系(Relationship)是指实体与实体之间的联系,有一对一、一对多、对多对三种情况。
例如,每个学生都有一个学生证,学生和学生证之间是一对一的联系;一个班级有多个学生,班级和学生是一对多的联系;一个学生可以选修多门课程,一门课程又可以被多个学生选修,学生和课程之间就形成了多对多的联系。
实体型
实体型(EntityType)即实体类型,通过实体名(如学生)及其属性名集合(如“学号、学生姓名、学生性别”)来抽象描述同类实体。
实体集
实体集(Entity Set)是指同一类型的实体集合,如全校学生就是一个实体集。
E-R图也称为实体-联系图(Entity Relationship Diagram),是一种用图形表示的实体联系模型,由Peter Chen于1976年提出。
E-R图提供了表示实体型、属性和联系的方法,用来描述现实世界的概念模型。其通用的表示方式如下:
实体: 用矩形框表示,将实体名写在框内。
属性: 用椭圆框表示,将属性名写在框内,用连线将实体与属性连接。
联系: 用菱形框表示,将联系名写在框内,用连线将相关的实体连接,并在连线旁标注联系类型(一 0 对 一 “1:1”、一对多 “1:n”、多对多 “n:m”)。
关系模型由IBM公司研究员 Edgar Frank Codd 于1970年发表的论文中提出,经过多年的发展,已经成为目前最常用、最重要的模型之一。在关系模型中有一些基本的概念。
关系
关系(Relation)一词与数学领域有关,它是集合基础上的一个重要的概念,用于反映元素之间的联系和性质。
从用户角度来看,关系模型的数据结构是二维表,即通过二维表来组织数据。一个关系对应一张二维表,表中的数据包括实体本身的数据和实体间的联系。
属性
二维表中的列称为属性(Attribute),每个属性都有一个属性名。
元组
二维表中的每一行数据称为一个元组(Tuple)。
根据不同的习惯,属性也可以称为字段(Field),元组也可以称为记录(Record)。
域
域(Domain)是指属性的取值范围。
关系模式
关系模式(Relation Schema)是关系的描述,通常可以简记为“关系名(属性1,属性2,…,属性n)。
键
在二维表中,若要唯一标识某一条记录,需要用到键(又称为关键字、码)。
为了保证数据库中数据的正确性和相容性,需要对关系模型进行完整性约束。完整性通常包括实体完整性、参照完整性和用户自定义完整性。
实体完整性
实体完整性要求关系中的主键不能重复,且不能取空值。
空值是指不知道、不存在或无意义的值。
由于关系中的元组对应现实世界中互相之间可区分的个体,这些个体使用主键来唯一标识,若主键为空或重复,则无法唯一标识每个个体。
参照完整性
参照完整性要求关系中的外键要么取空值,要么取被参照关系中的某个元组的主键值。
例如,通过学生所属的班级号可以找到对应的班级,这就符合参照完整性;而如果对应的班级被删除了,学生通过班级号找不到班级,就不符合参照完整性。
用户自定义完整性
用户自定义完整性是用户针对具体的应用环境定义的完整性约束条件,由DBMS检查用户自定义的完整性
关系模型可以使用关系代数(Relational Algebra)来进行关系运算。关系代数是一种抽象的查询语言,是研究关系模型的数学工具。关系代数运算符主要包括并、差、交、笛卡儿积、选择、投影、连接和除。
并(Union)、差(Difference)、交(Intersection)运算要求参与运算的两个关系具有相同数量的属性,其运算结果是一个具有相同数量属性的新关系。
选择(Selection)是在一个关系中将满足条件的元组找出来,即水平方向筛选。
投影(Projection)是在一个关系中去掉不需要的属性,保留需要的属性,即垂直方向筛选。
连接是在两个关系的笛卡儿积中选取属性间满足一定条件的元组。
由于笛卡儿积的结果可能会包含很多没有意义的元组,所以相比之下连接运算更为实用。
常用的连接方式有等值连接(Equi-Join)和自然连接(Natural Join)。
自然连接是一种特殊的等值连接,要求R和S必须有相同的属性组,进行等值连接后再去除重复的属性组。
如果把笛卡儿积看作乘法运算,则除法是笛卡儿积的逆运算。
R÷S1 表示查询学号为2的学生所选的课程;R÷S2 表示查询学号为2和3的学生共同选择的课程。
SQL (Structured Query Language,结构化查询语言) 是一种数据库查询语言和程序设计语言,主要用于管理数据库中的数据,如存取数据、查询数据、更新数据等。
SQL是IBM公司于1975-1979年开发出来的,在20世纪80年代,SQL被美国国家标准学会(ANSI)和国际标准化组织(International Organization for Standardization,ISO)定义为关系数据库语言的标准。
SQL是由4部分组成的。
数据定义语言
数据库定义语言(Data Definition Language,DDL)主要用于定义数据库、表等。
例如,CREATE语句用于创建数据库、数据表等,ALTER语句用于修改表的定义等,DROP语句用于删除数据库、删除表等。
数据操作语言
数据操作语言(Data Manipulation Language,DML)主要用于对数据库进行添加、修改和删除操作。
例如,INSERT语句用于插入数据,UPDATE语句用于修改数据,DELETE语句用于删除数据。
数据查询语言
数据查询语言(Data Query Language,DQL)主要用于查询数据。
例如,使用SELECT语句可以查询数据库中的一条数据或多条数据。
数据控制语言
数据控制语言(Data Control Language,DCL)主要用于控制用户的访问权限。
例如,GRANT语句用于给用户增加权限,REVOKE语句用于收回用户的权限,COMMIT语句用于提交事务,ROLLBACK语句用于回滚事务。
下载地址: https://dev.mysql.com/downloads/mysql/
本文安装的是mysql5.7.22版作为演示。
0️⃣解压下载好的mysql压缩包,我这里解压到C:\mysql5.7目录。
1️⃣打开命令提示符窗口,在命令模式下,切换到 MYSQL 安装目录下的bin目录
cd C:\mysql5.7\bin
2️⃣输入安装命令
mysqld -install
在安装MySQL时,还有一些常见的问题需要注意
MySQL安装的服务名默认为 “MySQL”,如果该名称已经存在,则会安装失败,提示The service already exists!
此时可能是系统中已经安装了MySQL,可以通过如下命令进行卸载,卸载后再进行安装。
mysqld -remove
MySQL允许在安装或卸载时指定服务名称,从而实现多个MySQL服务共存。
例如,当需要同时安装MySQL5.7和8.0时,分别指定不同的服务名称即可实现。
mysqld -install "服务名称"
mysqld -remove "服务名称"
MySQL服务默认监听3306端口,如果该端口被其他服务占用,会导致客户端无法连接服务器。
在命令行中可用 netstat -ano
命令查看端口占用情况。
为了获知该进程是哪一个程序,执行 tasklist | findstr "4204”
命令
当前是 mysqld.exe 占用了3306端口,说明MySQL服务正在工作。如果是其他程序占用了3306端口,只需将对应的服务停止即可。
3️⃣使用文本编辑器创建配置文件 C:\mysql5.7\my.ini,编写如下:
[mysqld]
basedir=C:\mysql5.7
datadir=C:\mysql5.7\data
port=3306
basedir 表示MySQL的安装目录;datadir 表示数据库文件的保存目录;port 表示MySQL服务的端口号。
在没有配置文件的情况下,MySQL会自动检测安装目录、数据文件目录。
但由于不同MySQL版本的路径可能有区别,所以建议通过配置文件来指定。
另外,Linux系统中通常使用 my.cnf 作为配置文件的文件名,在Windows系统中也可以使用该文件名。
4️⃣初始化数据库
创建my.ini配置文件后,数据库文件目录 C:\mysql5.7\data 还没有创建。接下来需要通过MySQL的初始化功能,自动创建数据文件目录
mysqld --initialize-insecure
“-initialize”表示初始化数据库,“-insecure”表示忽略安全性。
当省略 “insecure” 时,MySQL将自动为默认用户 “root” 生成一个随机的复杂密码,而加上 “insecure” 时,“root”用户的密码为空。由于自动生成的密码输人比较麻烦,因此这里选择忽略安全性。
MySQL5.5和5.6版本中已经提供了data目录,不需要初始化数据库。只有安装5.7和8.0版本时需要执行上述命令。
5️⃣管理MySQL服务
MySQL安装完成后,需要启动服务进程,否则客户端无法连接数据库。在前面的配置过程中,已经将MySQL安装为Windows服务。
MySQL服务不仅可以通过Windows服务管理器启动,还可以通过命令行来启动。
使用管理员身份打开命令提示符,输人如下命令启动名称为 MySQL 的服务。
net start MySQL
停止服务
net stop MySQL
6️⃣登录MYSQL
在 MySQL 的 bin 目录中,mysql.exe 是 MySQL 提供的命令行客户端工具,用于访问数据库。
该程序不能直接双击运行,需要打开命令行窗口,执行 cd C:\mysql5.7\bin 命令切换工作目录,然后执行如下命令登录MySQL服务器。
mysql -u root
“mysql”表示运行当前目录下的 mysql.exe。
“-u root” 表示以root用户的身份登录,其中,“-u”和“root”之间的空格可以省略。
如果需要退出MySQL,可以直接使用 exit 或 quit 命令。
命令行客户端工具还有一些常用选项。其中,“-h” 用于指定登录的MySQL服务器地址(域名或IP),如“-hlocalhost” 或 “-h 127.0.0.1” 表示登录本地服务器。选项“-P”(必须用大写字母P)用于指定连接的端口号,如“-P3306”表示连接3306端口。
7️⃣设置密码
为了保护数据库的安全,需要为登录MvSQL服务器的用户设置密码。
下面以设置root用户的密码为例,登录MySQL后,执行如下命令即可。
mysql>ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
上述命令表示为 localhost 主机中的 root 用户设置密码,密码为“123456”。
当设置密码后,退出MySQL,然后重新登录时,就需要输入刚才设置的密码。
在登录有密码的用户时,需要使用的命令如下。
mysql -uroot -p123456
在上述命令中,“-p123456” 表示使用密码 “123456” 进行登录。如果在登录时不希望密码被直接看到,可以省略“-p”后面的密码,然后按回车键,会提示输入密码。
在设置密码后,如果需要取消密码,可以使用如下命令。
mysql>ALTER USER 'root'@ 'localhost' IDENTIFIED BY ''
上述命令将密码设为空,即可免密码登录。
8️⃣设置环境变量
在启动 MYSQL 客户端前,确保命令提示符当前位于 C:\mysql5.7\bin 目录,执行命令将 bin 目录添加到环境变量。
setx PATH "%PATH%;C:\mysql5.7\bin"
执行上述命令后,关闭当前命令行窗口,重新打开一个新的命令行窗口即可生效。
创建数据库就是在数据库系统中划分一块存储数据的空间。
语法格式
CREATE DATABASE 数据库名称[库选项]
CREATE DATABASE 表示创建数据库;
数据库名称可以是字母、数字和下划线组成的任意字符串;
“库选项” 用于设置此数据库的相关特性,如字符集CHARSET,校对集COLLATE。
语法内使用 “[ ]” 括起来的选项表示可选参数。
下面创建一个名称为 mydb 的数据库,具体SQL语句与执行结果如下。
mysql>CREATE DATABASE mydb;
Query OK,1 row affected (0.00 sec)
在创建数据库后,MySQL.会在存储数据的data目录中创建一个与数据库同名的子目录(即mydb),同时会在mydb目录下生成一个 db.opt 文件,保存数据库选项,打开data\mydb\db.opt文件,如下所示。
default-character-set=latin1
default-collation-Latin1——swedish_ci
mydb 数据库的默认字符集为 latin1,校对集为 Latin1_swedish_ci。
创建的数据库已存在,则程序会报错,可以在创建数据库名称前添加 IN NOT EXISTS
。表示指定的数据库不存在时执行创建,否则忽略此操作。
查看所有的数据库
SHOW DATABASES
MySQL服务器已有4个数据库,这些数据库都是MySQL安装时自动创建的。
+-------------------+
|information_schema |
|performance_schema |
|mysql |
|sys |
+-------------------+
information_schema 和 performance_schema 数据库分别是 MySQL 服务器的数据字典(保存所有数据表和库的结构信息)和性能字典(保存全局变量等的设置)。
“mysql” 数据库主要负责MySQL服务器自己需要使用的控制和管理信息,如用户的权限关系等。
sys 是系统数据库,包括了存储过程、自定义函数等信息。
查看指定数据库的创建信息
SHOW CREATE DATABASE 数据库名称;
由于MySQL服务器中的数据需要存储到数据表中,而数据表需要存储到对应的数据库下,并且MYSQL服务器中又可以同时存在多个数据库,因此,在对数据和数据表进行操作前,首先需要选择数据库。
USE 数据库名称;
登录 MYSQL 时选择数据库
mysql -u 用户名 -p 密码 数据库名
DROP DATABASE 数据库名称;
若删除数据库不存在,mysql服务器会报错,可以在数据库名前添加 IF EXISTS
来判断删除的数据库是否存在,存在就删,不存在就不执行删除操作。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] 表名
(字段名 字段类型 [字段属性] …) [表选项]
TEMPORARY: 表示临时表,仅在当前会话中可见,并且在会话关闭时自动删除。
字段名:指的是数据表的列名。
字段类型:设置字段中保存的数据类型,如时间日期类型等。
字段属性:指的是字段的某些特殊约束条件。
表选项:用于设置表的相关特性,如存储引擎(ENGINE)、字符集(CHARSET)和校对集(COLLATE)。
在操作数据表之前,应该使用 USE 数据库名
指定操作是在哪个数据库中进行,否则会抛出 No database selected
错误。
例如:创建名为 goods 的数据表
create table goods (
id int comment '编号',
name varchar(32) comment '商品名'
);
INT用于设置字段数据类型是整型。
VARCHAR(L)表示可变长度的字符串,L表示字符数,如VARCHAR(32)表示可变的字符数是32。
COMMENT用于在创建表时添加注释内容,并将其保存到表结构中。
SHOW TABLES [LIKE 匹配模式];
若不添加可选项 LIKE匹配模式
,表示查看当前数据库中的所有数据表;
若添加则按照 匹配模式
查看数据表。其中,匹配模式符有两种,分别为 %
和 _
。前者表示匹配一个或多个字符,代表任意长度的字符串,长度也可以为0,后者仅可以匹配一个字符。
LIKE 后的匹配模式必须使用单引号或双引号包裹
查看数据表的相关信息
SHOW TABLES STATUS [FROM 数据库名] [LIKE 匹配模式];
修改数据表名称
语法格式1:
ALTER TABLE 旧表名 RENAME [TO|AS] 新表名;
语法格式2:
RENAME TABLE 旧表名1 TO 新表名1 [旧表名2 TO 新表名2] ...;
修改表选项
ALTER TABLE 表名 表选项 = 值;
查看数据表的字段信息
查看所有字段的信息
{DESCRIBE | DESC} 数据表名;
查看指定字段信息
{DESCRIBE | DESC} 数据表名 字段名;
查看数据的创建语句
SHOW CREATE TABLE 表名;
查看数据表结构
语法格式1:
SHOW [FULL] COLUMNS FROM 数据表名 [FROM 数据库名];
语法格式2:
SHOW [FULL] COLUMNS FROM 数据库名.数据表名;
可选项 FULL 表示显示详细内容,在不添加的情况下查询结果与 DESC 的结果相同。
在添加 FULL 选项时此语句不仅可以查看到 DESC 语句查看的信息,还可以查看到字段的权限、COMMENT字段的注释信息等。
修改字段名
ALTER TABLE 数据表名 CHANGE [COLUMN] 旧字段名 新字段名 字段类型 [字段属性];
数据类型表示新字段名的数据类型,不能为空,即使与旧字段的数据类型相同,也必须重新设置。
修改字段类型
ALTER TABLE 数据表名 MODIFY [COLUMN] 字段名 新字段类型 [字段属性];
修改字段的位置
ALTER TABLE 数据表名 MODIFY [COLUMN] 字段名1 数据类型 [字段属性] [FIRST | AFTER 字段名2];
新增字段
语法格式1:新增一个字段,并可指定其位置
ALTER TABLE 数据表名 ADD [COLUMN] 新字段名 字段类型 [FIRST | AFTER字段名];
语法格式2:同时新增多个字段
ALTER TABLE 数据表名 ADD [COLUMN] (新字段名1 字段类型1, 新字段名2 字段类型2,…);
在不指定位置的情况下,新增的字段默认添加到表的最后。另外,同时新增多个字段时不能指定字段的位置。
删除字段
ALTER TABLE 数据表名 DROP [COLUMN] 字段名;
DROP [TEMPORARY] TABLE [IF EXISTS] 数据表 1 [,数据表2] ...;
为所有的字段添加数据
严格按照数据表结构(字段的位置)插入对应的值。
INSERT [INTO] 数据表名 {VALUES | VALUE} (值1 [,值2] ...);
在MySQL中,若创建的数据表未指定字符集,则数据表及表中的字段将使用默认的字符集latin1。
因此,若用户插入的数据中含有中文,则会出现错误提示。
为为了解决以上中文插入的问题,通常在创建数据表时添加表选项,设置数据表的字符集。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] 表名 (字段名 字段类型[字段属性] ...) [DEFAULT] {CHARACTER SET[CHARSET} [=] utf8;
为部分字段添加数据
除了为数据表中所有字段添加数据外,还可以通过指定字段名的方式增加数据。
其中指定的字段名可以是数据表中全部的字段,也可以是部分的字段。
INSERT [INTO] 数据表名(字段名1 [,字段名2 ...) {VALUES | VALUE} (值1[, 值2] ...);
除此之外,MySQL中还提供了另外一种使用 INSERT 语句为指定字段添加数据的方式。
INSERT [INTO] 数据表名 SET 字段名1 = 值1 [,字段名2 = 值2] ...;
一次添加多行数据
INSERT [INTO] 数据表名 [(字段列表)] {VALUES | VALUE} (值列表)[,值列表] ...;
在多数据插入时,若一条数据插人失败,则整个插人语句都会失败。
查询表中全部数据
SELECT * FROM 数据表名;
查询表中部分字段
SELECT (字段名1, 字段名2, 字段名3, ...) FROM 数据表名;
简单条件查询数据
SELECT (字段名1, 字段名2, 字段名3, ...) FROM 数据表名 WHERE 字段名 = 值;
UPDATE 数据表名 SET 字段名1 = 值1[,字段名2 = 值2] [WHERE 条件表达式];
若没有where条件,那么表中对应的字段全部都会被修改
DELETE FROM 数据表名 [WHERE 条件表达式];
转义字符 | 含义 | 转义字符 | 含义 |
---|---|---|---|
\0 | 空字符(NULL) | \t | 制表符(HT) |
\r | 回车符(CR) | \b | 退格(BS) |
\n | 换行符(LE) | \’ | 单引号 |
\" | 双引号 | \% | %(常用于LIKE条件) |
\\ | 反斜线 | \_ | _(常用于LIKE条件) |
整数类型
数据类型 | 字节数 | 无符号数的取值范围 | 有符号数的取值范围 |
---|---|---|---|
TINYINT | 1 | 0~255 | - 128 ~ 127 |
SMALLINT | 2 | 0~65 535 | - 32 768 ~ 32 767 |
MEDIUMINT | 3 | 0~16 777 215 | - 8 388 608 ~ 8 388 607 |
INT | 4 | 0~4 294 967 295 | - 2 147 483 648 ~ 2 147 483 647 |
BIGINT | 8 | 0 ~ 18 446 744 073 709 551 615 | - 9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 |
不同整数类型所占用的字节数和取值范围都是不同的。
占用字节数最小的是TINYINT,占用字节数最大的是BIGINT。
不同整数类型的取值范围可以根据字节数计算出来。例如,TINYINT 类型的整数占用1字节,1字节是8位,那么,TINYINT 类型无符号数的最大值就是 2^8 - 1
(即255), 有符号数的最大值就是 2^7 - 1
(即127)。
若使用无符号数据类型,需要在数据类型右边加上 UNSIGNED
关键字来修饰,例如,INT UNSIGNED
表示无符号 INT 类型。
+------+-----------------+----+----+--------+------+
|Field |TYPE |NUll|KEY |Default |Extra |
+------+-----------------+----+----+--------+------+
|int_1 |int(11) |YES | |NULL | |
|int_2 |int(10) unsigned |YES | |NULL | |
+------+-----------------+----+----+--------+------+
数据类型右边使用小括号数字标注了显示宽度。默认情况下,显示宽度是取值范围所能表示的最大宽度。对于有符号类型,符号也占用一个宽度。
例如,255的显示宽度为3,一128的显示宽度为4。
❗显示宽度与取值范围无关,若数值的位数小于显示宽度,会填充空格,若大于显示宽度,则不影响显示结果。
为字段设置零填充(ZEROFILL)时,若数值宽度小于显示宽度,会在左侧填充0。
例如,在 my_Int 表中设置零填充和宽度
CREATE TABLE my_Int (
int_1 INT(3) ZEROFILL,
int_2 TINYINT(6) ZEROFILL
);
设置零填充后,字段自动设为无符号类型,这是因为负数不能使用负填充。
浮点数类型
数据类型 | 字节数 | 负数的取值范围 | 非负数的取值范围 |
---|---|---|---|
FLOAT | 4 | -3.402 823 466E + 38 ~-1.175 494 351E-38 | 0 和 1.175 494 351E-38~3.402 823 466E+38 |
DOUBLE | 8 | -1.797 693 134 862 315 7E + 308 ~ -2. 225 073 858 507 201 4E-308 | 0 和 2.225 073 858 507 201 4E-308~1.797 693 134 862 315 7E + 308 |
列举的取值范围是理论上的极限值,但根据不同的硬件或操作系统,实际范围可能会小。
当浮点数类型使用 UNSIGNED
修饰为无符号时,取值范围将不包含负数。
浮点数类型虽然取值范围很大,但是精度并不高。FLOAT的精度为6位或7位,DOUBLE 的精度大约为15位。如果超出精度,可能会导致给定的数值与实际保存的数值不一致,发生精度损失。
定点数类型
定点数类型(DECIMAL) 通过 DECIMAL(M,D) 设置位数和精度,其中,M表示数字总位数(不包括“.”和“—”),最大值为65,默认值为10;D表示小数点后的位数,最大值为30,默认值为0。
例如,DECIMAL(5,2) 表示的取值范围是 -999.99~999.99。
若小数部分超出范围,会进行四舍五人,并出现 Data truncated(数据截断) 警告。
若整数部分超出范围,数据会插人失败,提示 Out of range value(超出取值范围)错误。
浮点数类型也可以设置位数和精度,如 float(8,2),但仍有可能损失精度。在实际使用时应避免使用浮点数类型,以免出现不能人为控制的问题。因此,对于小数类型的设置,推荐使用定点数类型并设置合理的范围可以使计算更为准确。
BIT类型
BIT(位) 类型用于存储二进制数据,语法为BIT(M),M表示位数,范围为1~64。
数据类型 | 取值范围 | 日期格式 | 零 值 |
---|---|---|---|
YEAR | 1901 ~ 2155 | YYYY | 0000 |
DATE | 1000-01-01 ~ 9999-12-3 | YYYY-MM-DD | 0000-00-00 |
TIME | -838:59:59 ~ 838:59:59 | HH:MM:SS | 00:00:00 |
DATETIME | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 0000-00-00 00:00:00 |
TIMESTAMP | 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS | 0000-00-00 00:00:00 |
YYYY:表示年,MM:表示月,DD:表示日,每种日期和时间类型的取值范围都是不同的。
❗如果插入的数值不合法,系统会自动将对应的零值插人数据库中。
YEAR类型
YEAR 类型用于表示年份。
在 MySQL 中,可以使用以下 3 种格式指定 YEAR 类型的值。
'1901'~'2155'
或 1901~2155
。
'00'~'99'
,其中,'00'~'69'
的值会被转换为 2000~2069
的YEAR值,'70'~'99'
的值会被转换为 1970~1999
的YEAR值。
1~99
,其中,1~69
的值会被转换为 2001~2069
的YEAR值,70~99
的值会被转换为 1970~1999
的YEAR值。
❗当使用 YEAR 类型时,一定要区分 '0'
和 0
。因为字符串格式的 '0'
表示的 YEAR 值是 2000
,而数字格式的 0
表示的 YEAR 值是 0000
。
DATE类型
DATE 类型用于表示日期值,不包含时间部分。
在 MySQL 中,可以使用以下 4 种格式指定 DATE 类型的值。
'YYYY-MM-DD'
或者 'YYYYMMDD'
字符串格式表示。
'YY-MM-DD'
或者 'YYMMDD'
字符串格式表示。YY表示的是年,为 '00'~'99'
,其中 '00'~'69'
的值会被转换为2000~2069
的值,70'~'99'
的值会被转换为 1970~1999
的值。
YY-MM-DD
或者 YYMMDD
数字格式表示。
CURRENT_DATE
或者 NOW()
输入当前系统日期。通过 SELECT CURRENT_DATE
或 SELECT NOW()
可查看当前日期。
日期中的分隔符 -
还可以用 .
,
/
等符号替代。
TIME类型
TIME类型用于表示时间值,它的显示形式一般为 HH:MM:SS
,其中,HH表示小时,MM表示分,SS表示秒。
在 MySQL 中,可以使用以下 3 种格式指定 TIME 类型的值。
'HHMMSS'
字符串或者 HHMMSS
数字格式表示。
'345454'
或 345454
,插入数据库中的时间为 34:54:54(34小时54分54秒)。D HH:MM:SS
字符串格式表示。其中,D
表示日,可以取 0~34
之间的值,插入数据时,小时的值等于(D×24+HH)。
'2 11:30:50'
,插入数据库中的时间为 59:30:50
;输入 '11:30:50'
,插入数据库中的时间为11:30:50。CURRENT_TIME
或 NOW()
输入当前系统时间。DATETIME类型
DATETIME 类型用于表示日期和时间,它的显示形式为 'YYYY-MM-DD HH:MM:SS'
,其中,YYYY表示年,MM表示月,DD表示日,HH表示小时,MM表示分,SS表示秒。
在MySQL中,可以使用以下 4 种格式指定 DATETIME 类型的值。
以 'YYYY-MM-DD HH:MM:SS'
或者 'YYYYMMDDHHMMSS'
字符串格式表示的日期和时间,取值范围为'1000-01-01 00:00:00'
~'9999-12-31 23:59:59'
。
'2014-01-22 09:01:23'
或 20140122090123
,插入数据库中的 DATETIME 值都为 2014-01-22 09:01:23。以 'YYYY-MM-DD HH:MM:SS'
或者 'YYMMDDHHMMSS'
字符串格式表示的日期和时间,其中 YY 表示年,取值范围为 '00'~'99'
。与 DATE 类型中的 YY 相同,'00'~'69'
范围的值会被转换为 2000~2069
范围的值,'70'~'99'
范围的值会被转换为 1970~1999
范围。
以 YYYYMMDDHHMMSS
或者 YYMMDDHHMMSS 数字格式表示的日期和时间。
使用 NOW()
来输入当前系统的日期和时间。
TIMESTAMP类型
TIMESTAMP(时间戳) 类型用于表示日期和时间,它的显示形式与 DATETIME 相同、但取值范围比DATETIME小。
TIMESTAMP 类型与DATATIME类型不同的形式:
CURRENT_TIMESTAMP
来输入系统当前日期和时间。NULL
时,实际保存的是系统当前日期和时间。在MySQL中,TIMFSTAMP 字段默认情况下会自动设置 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
属性。
NOT NULL
表示非空约束,该字段将不允许保存 NULL
值。DEFAULT
表示默认约束,当字段无任何输入时,自动设置某个值作为默认值。此处设为CURRENT_TIMESTAMP
表示使用系统当前日期和时间作为默认值。ON UPDATE
用于当一条记录中的其他字段被 UPDATE
语句修改时,自动更改该字段为某个值。此处设为 CURRENT_TIMESTAMP
表示每次修改时保存修改时的系统日期和时间。若为
TIMESTAMP
字段手动设置DEFAULT
属性时,该字段将不会自动设置ON UPDATE
属性。
数据类型 | 类型说明 |
---|---|
CHAR | 固定长度字符串 |
VARCHAR | 可变长度字符串 |
TEXT | 大文本数据 |
ENUM | 枚举类型 |
SET | 字符串对象 |
BINARY | 固定长度的二进制数据 |
VARBINARY | 可变长度的二进制数据 |
BLOB | 二进制大对象(Binary Large Object) |
CHAR和VARCHAR类型
CHAR 和 VARCHAR 类型都用来保存字符串数据。不同的是,VARCHAR 可以存储可变长度的字符串。
CHAR(M)
VARCHAR(M)
M指的是字符串的最大长度。为了对比 CHAR 和 VARCHAR 之间的区别,下面以 CHAR(4) 和VARCHAR(4) 为例进行说明。
插入值 | CHAR(4) 存储需求 | VARCHAR(4) 存储需求 |
---|---|---|
‘’ | 4字节 | 1字节 |
‘ab’ | 4字节 | 3字节 |
‘abc’ | 4字节 | 4字节 |
‘abcd’ | 4字节 | 5字节 |
对于 CHAR(4) 无论插入值的长度是多少,所占用的存储空间都是4字节,而 VARCHAR(4) 占用的字节数为实际长度加1。
TEXT类型
TEXT类型用于保存大文本数据,例如,文章内容、评论等比较长的文本。
数据类型 | 存储范围 | 数据类型 | 存储范围 |
---|---|---|---|
TINYTEXT | 0~2^8 - 1 字节 | MEDIUMTEXT | 0~2^24-1 字节 |
TEXT | 0~2^16 - 1 字节 | LONGTEXT | 0~2^32-1 字节 |
❗TEXT类型所能保存的最大字符数量取决于字符串实际占用的字节数。
ENUM类型
ENUM 类型又称为枚举类型。
ENUM('值1', '值2', '值3', ...,'值n')
('值1', '值2', '值3', ...,'值n')
称为枚举列表,ENUM类型的数据只能从枚举列表中取,并且只能取一个。
在MySQL中,枚举列表最多可以有 65535
个值,每个值都有一个顺序编号,实际保存在记录中的是顺序编号,而不是列表中的值,因此不必担心过长的值占用空间。但在使用 SELECT、INSERT 等语句进行操作时,仍然使用列表中的值。
SET类型
SET类型用于保存字符串对象,其定义格式与ENUM类型类似。
SET('值1', '值2', '值3', ...,'值n')
SET类型的列表中最多可以有 64
个值,且列表中的每个值都有一个顺序编号,为了节省空间,实际保存在记录中的也是顺序编号,但在使用 SELECT、INSERT 等语句进行操作时,仍然要使用列表中的值。
SET类型与ENUM的区别在于,它可以从列表中选择一个或多个值来保存,多个值之间用逗号 ,
分隔。
1️⃣ENUM 类型类似于单选框,SET 类型类似于复选框。
2️⃣ENUM 和 SET 类型的优势在于规范数据本身,限定只能插入规定的数据项,节省了存储空间,查询速度比 CHAR、VARCHAR 类型快。
3️⃣ENUM 和 SET 类型列表中的值都可以使用中文,但必须设置支持中文的字符集,例如CREATE TABLE my_enum (gender ENUM('男', '女')) CHARSET=GBK;
。
4️⃣ENUM 和 SET 类型在填写列表、插入值、查找值等操作时,都会自动忽略末尾的空格。
BINARY和VARBINARY类型
BINARY 和 VARBINARY 类型类似于 CHAR 和 VARCHAR,不同的是,它们所表示的是二进制数据。
BINARY(M)
VARBINARY(M)
M 是指二进制数据的最大字节长度。
BINARY 类型的长度是固定的,如果数据的长度不足最大长度,将在数据的后面用 \0
补齐,最终达到指定长度。
例如,指定数据类型为 BINARY(3)
,当插入a
时,实际存储的数据为 a\0\0
,当插入 ab
时,实际存储的数据为 ab\0
。
在查询 BINARY 类型时,查询条件字符串也需要加上 \0
填充符,否则查询不到该记录,并且BINARY 和 VARBINARY 都区分大小写。
例如,指定数据类型为 BINARY(3)
,当查询 a
时,WHERE条件值要输入 a\0\0
,否则查询不到该记录。
BLOB类型
BLOB类型用于保存数据量很大的二进制数据,如图片、PDF文档等。
数据类型 | 存储范围 | 数据类型 | 存储范围 |
---|---|---|---|
TINYBLOB | 0~2^8-1字节 | MEDIUMBLOB | 0~2^24-1字节 |
BLOB | 0~2^16-1字节 | LONGBLOB | 0~2^32-1字节 |
BLOB 类型与 TEXT 类型很相似,但 BLOB 类型数据是根据二进制编码进行比较和排序,而 TEXT 类型数据是根据文本模式进行比较和排序。
MySQL从 5.7.8 版本开始提供了JSON 数据类型。
JSON是一种轻量级的数据交换格式,由 JavaScript 语言发展而来,其本质是一个字符串。
MySQL中 JSON 类型值常见的表现方式有两种,分别为 JSON数组 和 JSON对象。
# 1. JSON 数组
["abc", 10, null, true, false]
# 2.JSON对象
["k1", "value", "k2", "10"]
JSON数组中保存的数据可以是任意类型。
与直接使用 MySQL 字符串类型相比,JSON 数据类型具有自动验证格式、优化存储格式的优点。
JSON 数据类型所需的空间大致与 LONGBLOB 或 LONGTEXT 相同,且不能有默认值。
CREATE TABLE myjson (j1 JSON, j2 JSON);
INSERT INTO myjson VALUES (
'{"k1", "value", "k2", "10"}, ["run", "sing"]'
);
若要在 CHAR、VARCHAR、TEXT 类型的字符串比较时严格区分大小写,有多种实现方式。下面两种比较常见的方式。
使用 BINARY 关键字
在字段名或某个值的前面加上 BINARY 关键字可以将类型转换为二进制,转换后进行比较就可以严格区分大小写和空格。
SELECT 'a ' = 'A'; #比较结果:1(相等)
SELECT BINARY 'a' = 'A', 'a' = BINARY 'A'; #比较结果:0(不相等)
SELECT BINARY 'A ' = 'A'; #比较结果:0(不相等)
SELECT BINARY 'A' = 'A', 'A' = BINARY 'A'; #比较结果:1(相等)
设置字段的校对集
latin1、gbk、utf8 编码默认的校对集分别为 latin1_swedish_ci、gbk_chinese_ci、utf8_general_ci,将其分别改为 latin1_bin、gbk_bin、utf8_bin 即可区分大小写,但在比较时仍会忽略字符串末尾的空格。
# 1.创建表时设置字段的校对集
CREATE TABLE mychar (
c1 CHAR(2) CHARACTER SET latin1 COLLATE latin1_bin,
c2 CHAR(2) CHARACTER SET gbk COLLATE gbk_bin,
c3 CHAR(2) CHARACTER SET utf8 COLLATE utf8_bin
);
# 2.插入测试数据
INSERT INTO mychar VALUES('A', 'A', 'A')
# 3.查询数据
SELECT c1 = 'a', c2 = 'a', c3 = 'a' FROM mychar; #结果:0(不想等)
SELECT c1 = 'A', c2 = 'A', c3 = 'A' FROM mychar; #结果:1(想等)
SELECT c1 = 'A ', c2 = 'A ', c3 = 'A ' FROM mychar; #结果:1(想等)
1️⃣CHAR 和 VARCHAR 类型在插入数据时,若字符串末尾有空格,CHAR 类型会自动去掉空格后保存,而VARCHAR、TEXT 类型会保留空格。
2️⃣在使用 “=” 等运算符对 CHAR 、VARCHAR 、TEXT 进行比较时,字符串末尾的空格会被忽略。
例如,使用 WHERE 查询 ‘a’ 字符串,查询结果中可能包含 a 后面有空格的情况,反之,若查询条件字符串末尾有空格(如 'a '),空格也会被忽略。
3️⃣由于默认情况下创建的数据库和表使用的校对集 (latin1_swedish_ci) 对大小写不敏感,因此CHAR、VARCHAR、TEXT、ENUM、SET 类型都不区分大小写。
例如,使用 WHERE 查询 ‘a’ 字符串,则 “a” 和 “A” 都会被查询出来。而 BINARY、VARBINARY、BLOB类型区分大小写,这是因为它们使用二进制方式保存数据。
4️⃣MySQL 默认规定一条记录的最大长度是 65535 字节,一般来说,字段分配的存储空间和额外开销加在一起不能超过 65535 字节,如果超过了这个限制,在SQL 严格模式下表会创建失败,提示 Row size too large。但 TEXT 和 BLOB 类型字段的存储空间不受此限制,它们只占用额外开销(大约12字节)。
5️⃣在没有超过 65535 限制的情况下,CHAR 字段的 M 最大值为255,VARCHAR字段的 M 的最大值取决于字符集,常用的字符集有 latin1(默认)、gbk 和 utf8,对应的M最大值分别为 65533、32766 和 21844,若表中只有一个字段且设置了非空约束,M 可达到最大值,否则 M 的最大值会减小。
6️⃣从执行效率上来说,TEXT 和 BLOB 不如 CHAR、VARCHAR 类型,建议只有当需要保存大量数据时,才选择使用 TEXT 或 BLOB 类型。
为了防止数据表中插入错误的数据,MySQL定义了一些维护数据库完整性的规则,即表的约束。
默认约束用于为数据表中的字段指定默认值,即当在表中插人一条新记录时,如果没有给这个字段赋值,那么,数据库系统会自动为这个字段插入默认值。
字段名 数据类型 DEFAULT 默认值;
使用 ALTER TABLE 修改列属性即可添加或删除默认约束。
BLOB、TEXT 数据类型不支持默认约束。
非空约束指的是字段的值不能为NULL。
字段名 数据类型 NOT NULL;
为现有的表添加或删除非空约束的方式与默认约束类似,使用ALTER TABLE修改列属性即可。
若目标列中已经保存了NULL值,添加非空约束会失败,提示 “Invalid use of NULL value”,只要将NULL值改为其他值即可解决。
唯一约束用于保证数据表中字段的唯一性,即表中字段的值不能重复出现。
#列级约束
字段名 数据类型 UNIQUE;
#表级约束
UNIQUE(字段名1,字段名2, ...);
列级约束定义在一个列上,只对该列起约束作用;表级约束是独立于列的定义,可以应用在一个表的多个列上。
当表级约束仅建立在一个字段上时,其作用效果与列级约束相同。
若为一个现有的表添加或删除唯一约束,无法通过修改字段属性的方式操作,而是按照索引的方式来操作。
添加唯一约束的完整语法
UNIQUE KEY 索引名(字段列表)
添加唯一约束时创建索引,用于加快查询速度。
索引名可以自己指定,也可以省略,MySQL会自动使用字段作为索引名。当需要对索引进行删除时,需要指定这个索引名。
在表级唯一性约束创建时,UNIQUE() 的字段列表中,可以添加多个字段,组成复合唯一键,其特点是只有多个字段的值相同时才视为重复记录。
为了快速查找表中的某条信息,可以通过设置主键来实现。主键可以唯一标识表中的记录,类似身份证用于标识人的身份一样。
主键约束通过 PRIMARY KEY 定义,它相当于唯一约束
和非空约束
的组合,要求被约束字段不允许重复,也不允许出现 NULL 值
,每个表最多只允许含有一个主键
。
# 列级约束
字段名 数据类型 PRIMARY KEY
#表级约束
PRIMARY KEY(字段名1,字段名2,…)
表级约束的字段若只有一个,则为单字段主键与列级约束添加的效果相同;若有多个,则为复合主键。复合主键需要用多个字段来确定一条记录的唯一性,类似于复合唯一键。
# 1.删除主键约束
mysql>ALTER TABLE my_primary DROP PRIMARY KEY;
# 2.删除id字段的非空约束
mysql>ALTER TABLE my_primary MODIFY id INT UNSIGNED;
# 3.添加主键约束
mysql>ALTER TABLE my_primary ADD PRIMARY KEY(id);
删除 id 字段的主键约束后,该字段的非空约束并没有被同时删除。若需要删除 id 字段的非空约束,执行第2步删除操作即可。
为数据表设置主键约束后,每次插入记录时,都需要检查主键的值,防止插入的值重复导致插入失败,可以利用MySQL提供的自动增长功能来自动生成主键的值。
字段名 数据类型 AUTO_INCREMENT
在使用 AUTO_INCREMENT 时,需要注意以下4点:
一个表中只能有一个自动增长字段,该字段的数据类型是整数类型,且必须定义为键,如 UNIQUE KEY
、PRIMARY KEY
。
若为自动增长字段插入 NULL
、0
、DEFAULT
或在插入时省略该字段,则该字段就会使用自动增长值;若插入的是一个具体值,则不会使用自动增长值。
自动增长值从 1 开始自增,每次加 1。若插入的值大于自动增长的值,则下次插入的自动增长值会自动使用最大值加 1;若插入的值小于自动增长值,则不会对自动增长值产生影响。
使用 DELETE 删除记录时,自动增长值不会减小或填补空缺。
查看自动增长值
SHOW CREATE TABLE 数据库名\G
修改自动增长
ALTER TABLE 数据库名 AUTO_INCREMENT = 值;
删除自动增长
ALTER TABLE 数据库名 MODIFY 字段名 字段类型 [字段属性];
添加自动增长
ALTER TABLE 数据库名 MODIFY 字段名 字段类型 [字段属性] AUTO_INCREMENT;
通过
SHOW VARIABLES LIKE 'auto_increment%';
可以查看 MySQL 中用于维护自动增长的变量分别是auto increment_ increment(默认值1)
和auto_increment_offset(默认值为1)
,通过更改这两个变量可以改变自动增长的计算方式,可以参考官方手册中的详细说明。
MySQL默认使用的字符集为latin1(ISO-8859-1的别名),这是一种单字节编码的字符集。
由于中文汉字属于多字节编码的字符,若要保存中文,需要设置成其他支持中文的字符集,如GBK、UTF8。
校对集是字符之间的比较关系,在比较、排序等操作时都会用到校对集。
字符集
字符(character)指计算机中保存的各种文字和符号,包括各种国家的文字、标点符号、图形符号、数字等。
由于计算机采用二进制保存数据,用户输入的字符将会按照一定的规则转换为二进制后保存,这个过程就是字符编码(character encoding),将一系列字符的编码规则组合起来就形成了字符集(character set,charset)。
MySQL提供了各种字符集的支持,通过 SHOW CHARACTER SET;
可以查看可用字符集。
常用字符集
字符集 | 单字符最大长度 | 支持的语言 |
---|---|---|
latin1 | 1字节 | 西欧字符、希腊字符等 |
gbk | 2字节 | 简体和繁体中文、日文、韩文等 |
utf8 | 3字节 | 世界上大部分国家的文字 |
单字符占用的存储空间越多,所支持的语言越多。
表中列举的字符集都向下兼容 ASCII(美国信息交换标准代码)
,若输入的字符在 ASCII 范围内(0x00~0x7F),则编码结果是完全相同的。
由于历史原因,MySQL 中的 utf8 编码与标准的 UTF-8(RFC3629) 存在一些差别。标准的 UTF-8 规定一个字符最多使用 4 字节,而 MySQL 中的 utf8 编码一个字符最多使用 3 字节,这导致 UTF-8 中的一些特殊字符(如emoji符号)无法在 MySQL 的 utf8 编码中使用。为了解决这个问题,MySQL从5.5.3版本开始新增了 utf8mb4 编码,将一个字符扩展到4字节。因此,如果要考虑支持 RFC3629 规范,应使用 utf8mb4 编码。
校对集
MySQL中提供了许多校对集,用于为不同字符集指定比较和排序规则。
例如,latin1字符集默认的校对集为 latin1_swedish_ci
。
校对集的名称由 _
分隔的3部分组成,开头是对应的字符集,中间是国家名或 general,结尾是 ci、cs或bin。其中,ci 表示不区分大小写,cs 表示区分大小写,bin 表示以二进制方式比较。
通过 SHOW COLLATION;
可以查看 MySQL 可用的校对集。
MySQL环境
使用 MySQL 命令 SHOW VARIABLES LIKE 'character%';
可以查看与字符集相关的变量。
+-------------------------------+--------------------------------+
| Variable_name |Value |
+-------------------------------+--------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | C:\mysql5.7\share\charsets\ |
+-------------------------------+--------------------------------+
上述结果表示当前会话(Session)使用的字符集,在不同客户端环境中的输出结果可能不同。
这里所说的会话,是指从客户端登录服务器到退出的整个过程。例如,依次打开两个客户端并登录服务器,就产生了两个会话,不同客户端属于不同的会话。
不同的客户端可以指定不同的字符集环境配置,服务器会按照不同的配置进行处理。
变量名 | 说 明 |
---|---|
character_set_client | 客户端字符集 |
character_set_connection | 客户端与服务器连接使用的字符集 |
character_set_database | 默认数据库使用的字符集(从5.7.6版本开始不推荐使用) |
character_set_filesystem | 文件系统字符集 |
character_set_results | 将查询结果(如结果集或错误消息)返回给客户端的字符集 |
character_set_server | 服务器默认字符集 |
character_set_system | 服务器用来存储标识符的字符集 |
character_sets_dir | 安装字符集的目录 |
重点关注的变量是 character_set_client
、 character_set_connection
、character_set_results
和 character_set_server
。
character_set_server 决定了新创建的数据库默认使用的字符集。数据库的字符集决定了数据表的默认字符集,数据表的字符集决定了字段的默认字符集。
character_set_client、character_set_connection 和character_set_results 分别对应客户端、连接层和查询结果的字符集。
通常情况下,这3个变量的值是相同的,具体值由客户端的编码而定,从而使客户端输入的字符和输出的查询结果都不会出现乱码。
通过 set 变量名 = 值;
命令可以更改变量的值。
set character_set_client=gbk;
set character_set_connection=gbk;
set character_set_results=gbk;
上述命令输入比较麻烦,在 MySQL 中还可以通过 set names 字符集;
直接修改上述3个变量的值,即set names gbk;
。
注意:
使用 set或 set names 修改字符集只对当前会话有效,不影响其他会话,且会话结束后,下次会话仍然使用默认值。
与 character_set_connection、character_set_database、character_set_server 对应的校对集分别通过变量 collation_connection、collation_database、collation_server来指定。
若字段使用 utf8 字符集,而客户端使用 gbk 字符集,MySQL 会自动进行编码转换。由于 utf8 和 gbk 本质上是不同的字符集,虽然大部分常见的字符可以转换成功,但若遇到其中一个字符集中没有的特殊字符,则可能会出现乱码。
数据库
在创建数据库时设定字符集和校对集。
[DEFAULT] CHARACTER SET [=] charset_name
[DEFAULT] COLLATE [=] collation_name
CHARACTER SET 用于指定字符集,COLLATE 用于指定校对集。
若仅指定字符集,表示使用该字符集的默认校对集;若仅指定校对集,表示使用该校对集对应的字符集。
示例
# 创建数据库,指定字符集为 utf8,使用默认校对集 utf8_general_ci
CREATE DATABASE 数据库名 CHARACTER SET utf8;
# 创建数据库,指定字符集为 utf8,校对集为 utf8_bin
CREATE DATABASE 数据库名 CHARACTER SET utf8 COLLATE utf8_bin;
数据表
数据表的字符集与校对集在表选项中设定。
[DEFAULT] CHARACTER SET [=] charset_name
[DEFAULT] COLLATE [=] collation_name
若没有为数据表指定字符集,则自动使用数据库的字符集。
字段
字段的字符集与校对集在字段属性中设定。
[CHARACTER SET charset_name] [COLLATE collation_name]
若没有为字段设定字符集与校对集,则会自动使用数据表的字符集与校对集。
示例
CREATE TABLE 数据表名 (
username VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_bin
);
数据库设计要求设计人员对数据库有深入的理解,才能设计出高质量的数据库。数据库设计一般分为 6 个阶段,分别是需求分析、概念数据库设计、逻辑数据库设计、物理数据库设计、数据库实施、数据库运行和维护。
需求分析
需求分析阶段,数据库设计人员需要分析用户的需求,将分析结果记录下来,形成需求分析报告。
在这个阶段中,双方需要进行深入的沟通,以避免理解不准确导致后续的工作出现问题。
在需求分析中有许多琐碎、耗时的工作,常见的工作如下:
概念数据库设计
在概念数据库设计阶段,将对用户的需求进行综合、归纳、抽象,形成概念模型。
概念模型使设计人员摆脱数据库系统的具体技术问题,将精力集中在分析数据及数据之间联系等方面。一般通过绘制E-R图,直观呈现数据库设计人员对用户需求的理解。
逻辑数据库设计
逻辑数据库设计面向数据库系统,在概念数据库设计中完成 E-R 图等成果后,将其转换为 DBMS 支持的数据模型(如关系模型),完成实体、属性和联系的转换。
在进行逻辑数据库设计时,应遵循一些规范化理论,如范式。不规范的设计可能会导致数据库出现大量冗余、插入异常、删除异常等问题。
物理数据库设计
物理数据库设计阶段需要确定数据库的存储结构、文件类型等。
通常DBMS为了保证其独立性与可移植性,承担了大部分任务,数据库设计人员只需要考虑硬件、操作系统的特性,为数据表选择合适的存储引擎,为字段选择合适的数据类型等,以及评估磁盘空间需求等工作。
数据库实施
数据库实施就是将前面那些工作的成果实施起来,比如使用SQL语句创建数据库、数据表,编写与调试应用程序等。
数据库运行和维护
数据库运行和维护就是将数据库系统正式投入运行,在运行后进行一些维护、调整、备份、升级等工作。
数据库设计对数据的存储性能、数据的操作都有很大的关系。为了避免不规范的数据库出现数据冗余,造成插入、删除、更新操作异常等情况,就要满足一定的规范化要求,这就是范式(Normal Form)。
根据要求的程度不同,范式有多种级别,最常用的有第一范式(1NF)、第二范式(2NF) 和 第三范式(3NF),它们由 Edgar Frank Codd 于1971年相继提出。后来,又有人提出了 Boyce-Codd 范式(BCNF)、第四范式(4NF) 和 第五范式(5NF)等。
一般来说,数据库设计只需满足第三范式(3NF)就可以了。
第一范式(1NF) 是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值,或不能有重复的属性。
简而言之,第一范式遵从原子性,属性不可再分。
演示
❌不满足第一范式的情况。
表1 的问题在于 “联系方式” 包含了多个值,可以再细分;表2 的问题在于 “手机号” 属性重复。
✔️为了满足第一范式,应将用户和联系方式分成两个表保存,两个表是一对多的联系,如下图所示。
第二范式(2NF) 是在第一范式的基础上建立起来的,满足第二范式必须先满足第一范式。
第二范式要求实体的属性完全依赖于主键,不能仅依赖主键的一部分(对于复合主键而言)。
简而言之,第二范式遵从唯一性,非主键字段需完全依赖主键。
演示
❌不满足第二范式的情况。
表格中倾斜的字体为主键。
在表6中,用户编号和订单编号组成了复合主键,付款状态完全依赖复合主键,而用户名只依赖用户编号。
采用上述方式设计的用户表存在如下问题:
✔️为了满足第二范式,将复合主键移动到订单表中,如下图所示。
第三范式(3NF)是在第二范式的基础上建立起来的,即满足第三范式必须先满足第二范式。
第三范式要求一个数据表中每一列数据都和主键直接相关,而不能间接相关。
简而言之,第三范式就是非主键字段不能相互依赖。
演示
❌不满足第三范式的情况。
在表9中,用户享受的折扣与用户等级相关,两者存在依赖关系。
采用这种方式设计的用户表存在如下问题:
✔️为了满足第三范式,将等级与折扣拆分到单独的表中,如下图所示。
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] 表名 LIKE 旧表名;
在上述语法中,仅能从 旧表名
中复制一份相同
的表结构,但不会复制表中保存的数据。
复制已有的表数据
数据复制也可称为蠕虫复制,是新增数据的一种方式,它是从已有的数据中获取数据,并且将获取到的数据插入到对应的数据表中,实现成倍的增加。
此种方式获取数据与插入数据的表结构要相同,否则可能会遇到插入不成功的情况。
INSERT [INTO] 数据表名1 [(字段列表)] SELECT [(字段列表)] FROM 数据表名2;
❗若数据表中含有主键,而主键又具有唯一性,所以在数据复制时还要考虑主键冲突的问题。
临时表指的是一种仅在当前会话中可见,并在当前会话关闭时自动删除的数据表。它主要用于临时存储数据。
临时表的语法很简单,只需在CREATE与TABLE关键宇中间添加 TEMPORARY
即可。
# 方式1:创建临时表
CREATE TEMPORARY TABLE mydb.tem1 (id INT);
#方式2:创建临时表
CREATE TEMPORARY TABLE mydb.tem2 SELECT id, name FROM mydb.goods ;
在上迷语句中,创建临时表时指定的数据库可以是 MySQL 服务器中存在的数据库,也可以是不存在的数据库。
若数据库不存在,操作临时表时必须使用 数据库.临时表名
指定临时表所在的数据库。
临时表中数据的操作与普通表相同,都可以进行 SELECT、INSERT、UPDATE 和 DELETE 操作。
SHOW TABLES 不能查看指定数据库下有哪些临时表,并且临时表的表名必须使用 ALTER TABLE 修改,而不能使用 RENAME TABLE … TO 修改。
在对数据表插人数据时,若表中的主键含有实际的业务意义,因此在插入数据时若不能确定对应的主键是否存在,往往会出现主键冲突的情况。
例如,mydb.my_goods 表经过数据复制以后,再插入编号为 20 的商品信息(橡皮,文具类,用于修正书写错误),具体SQL语句及执行结果如下。
INSERT INTO mydb.my_goods(id, name, content, keyword) VALUES (20, '橡皮', '用于修正书写错误', '文具');
ERROR 1062 (23000): Duplicate entry '20' for key 'PRIMARY'
从上述的执行结果可知,系统提示插入数据的主键发生冲突。若要解决这类问题,MySQL中提供了两种方式,分别为主键冲突更新和主键冲突替换。
主键冲突更新
主键冲突更新操作指的是,当插人数据的过程中若发生主键冲突,则插入数据操作利用更新的方式实现。
INSERT [INTO] 数据表名 [(字段列表)] {VALUES | VALUE} (值列表) ON DUPLICATE KEY UPDATE 字段名1 = 新值1 [,字段名2 = 新值2] ...;
在 INSERT
语句后添加 ON DUPLICATE KEY UPDATE
可在发生主键冲突时,更新此条记录中通过 字段名1 = 新值1 [,字段名2 = 新值2] ...
设置的字段名对应的新值。
例如,修改以上发生主键冲突的插入语句。
INSERT INTO mydb.my_goods(id, name, content, keyword) VALUES (20, '橡皮', '用于修正书写错误', '文具') ON DUPLICATE KEY UPDATE name = '橡皮', content = '用于修正书写错误', keyword = '文具';
主键冲突替换
主键冲突替换操作指的是,当插入数据的过程中若发生主键冲突,则删除此条记录,并重新插入。
REPLACE [INTO] 数据表名 [(字段列表)] {VALUES | VALUE} (值列表) [,(值列表)] ...;
REPLACE 语句与 INSERT 语句的使用类似,区别在于前者每执行一次就会发生两个操作(删除记录和插入记录)。
具体SQL语句及执行结果如下。
REPLACE INTO mydb.my_goods(id, name, content, keyword) VALUES (20, '橡皮', '用于修正书写错误', '文具');
TRUNCATE [TABLE] 表名;
TRUNCATE 操作虽然与 DELETE 语句的使用非常相似,但是两者在本质上有一定的区别,具体如下。
实现方式不同
执行效率不同
对 AUTO_INCREMENT 的字段影响不同
删除数据的范围不同
返回值含义不同
所属SQL语言的不同组成部分
-DELETE语句属于DML数据操作语句,而 TRUNCATE 通常被认为是 DDL 数据定义语句。
当删除的数据量很小时,DELETE 的执行效率要比 TRUNCATE 高;只有删除的数据量很大时,才能看出 TRUNCATE 的执行效率比 DELETE 高。
SELECT select选项 字段列表 FROM 数据表
select选项 默认值为 All
,表示保存所有查询到的记录;当设置为 DISTINCT
时,表示去除重复记录,只保留一条。
❗当查询记录的字段有多个时,必须所有字段的值完全相同才被认为是重复记录。
单字段排序
单字段排序指的是查询时仅按照一个指定字段进行升序或降序排序。
SELECT [* | (字段列表)] FROM 数据表名 ORDER BY 字段名 [ASC | DESC];
ASC
表示升序,DESC
表示降序,而 ORDER BY
默认值为 ASC
。
多字段排序
当在开发中需要根据多个条件对查询的数据进行排序时,可以采用多字段排序。
SELECT [* | (字段列表)] FROM 数据表名 ORDER BY 字段名1 [ASC | DESC] [, 字段名2 [ASC | DESC]];
多字段排序首先按照 字段名1 进行排序,当字段1 的值相同时,再按照 字段名2 进行排序,依此类推。
数据表的字符集是 utf8
,当排序的字段为中文时,默认不会按照中文拼音的顺序排序。那么在不改变数据表结构的情况下,可以使用 CONVERT(字段名 USING gbk)
函数强制让指定的字段按中文排序。
在按照指定字段进行升序排列时,如果某条记录的字段值为 NULL
,则系统会将 NULL
看作是最小的值,从而将其显示在查询结果中的 第一条
记录的位置。
对于一次性查询出的大量记录,不仅不便于阅读查看,MySQL中提供了一个关键字 LIMIT
,可以限定记录的数量,也可以指定查询从哪一条记录开始。
SELECT [select选项] 字段列表 FROM 数据表名 [WHERE条件表达式] [ORDER BY 字段 ASC | DESC] LIMIT [OFFSET,] 记录数;
记录数 表示限定获取的最大记录数量,也就是说,在记录数大于数据表符合要求的实际记录数量时,以实际记录数为准。
当 LIMIT 后仅含有此参数时,表示从第1条记录开始获取。
OFFSET 表示偏移量,用于设置从哪条记录开始,MySQL中默认第1条记录的偏移量值为0,第2条记录的偏移量值为1,依此类推。
排序后限量更新或删除数据
在MySQL中除了对查询记录进行排序和限量外,对数据表中记录的更新与删除操作也可以进行排序和限量。
# 数据更新的排序与限量
UPDATE 数据表名 SET 字段 = 新值, ... [WHERE 条件表达式] ORDER BY 字段 ASC|DESC LIMIT 记录数;
#数据删除的排序与限量
DELETE FROM 数据表名[WHERE 条件表达式] ORDER BY 字段 ASC|DESC LIMIT 记录数;
UPDATE和 DELETE 操作中添加 ORDER BY 表示根据指定的字段,按顺序更新或删除符合条件的记录。如果 UPDATE 和 DELETE 操作没有添加 WHERE 条件,则可以使用 LIMIT 来限制更新和删除的数量。
例如,为sh_goods表中价格最便宜的两种商品库存设置为500。
UPDATE sh_goods SET stock = 500 ORDER BY price ASC LIMIT 2;
在MySQL中执行查询操作时,可以根据具体情况为获取的字段设置别名。
SELECT 字段1 [AS] 别名1, 字段2 [AS] 别名2 [, ...] FROM 表名;
AS 用于为其前面的字段、表达式、函数等设置别名。也可以省略AS使用空格代替。
在MySQL中还可以使用 AS 为数据表设置别名。
SELECT 表别名.字段 [, ...] FROM 表名 [AS] 表别名
同样地,在为数据表设置别名时,AS也可以省略,使用空格代替。
字段与表设置别名后,在排序和分组中可以使用原来的字段名等,也可以使用别名。表的别名主要在多表查询中使用。
常用的聚合函数
函 数 名 | 描 述 |
---|---|
COUNT() | 返回参数字段的数量,不统计为NULL的记录 |
SUM() | 返回参数字段之和 |
AVG() | 返回参数字段的平均值 |
MAX() | 返回参数字段的最大值 |
MIN() | 返回参数字段的最小值 |
GROUP_CONCAT() | 返回符合条件的参数字段值的连接字符串 |
JSON_ARRAYAGG() | 将符合条件的参数字段值作为单个JSON数组返回,MySQL5.7.22新增 |
JSON_OBJECTAGG() | 将符合条件的参数字段值作为单个JSON对象返回,MySQL5.7.22新增 |
COUNT()、SUM()、AVG()、MAX()、MIN() 和 GROUP_CONCAT() 函数中可以在参数前添加DISTINCT,表示对不重复的记录进行相关操作。
COUNT() 的参数设置为 *
时,表示统计符合条件的所有记录(包含NULL)。
在查询数据时,在 WHERE 条件后添加 GROUP BY 即可根据指定的字段进行分组。
SELECT [select选项] 字段列表 FROM 数据表名 [WHERE 条件表达式] GROUP BY 字段名;
在MySQL中,默认情况下为分组操作的字段提供了升序排序的功能,因此在分组时可以为指定的字段进行升序或降序排序。
SELECT [select选项] 字段列表 FROM 数据表名 [WHERE 条件表达式] GROUP BY 字段名[ASC | DESC];
GROUP BY 分组排序的实现不需要使用 ORDER BY,直接在分组字段后添加 ASC(升序,默认值可省略) 或 DESC(降序) 即可。
在对数据进行分组统计时,MySQL中还支持数据按照某个字段进行分组后,对已经分组的数据进行再次分组的操作,以实现多分组统计。
SELECT [select选项] 字段列表 FROM 数据表名 [WHERE 条件表达式] GROUP BY 字段名1 [ASC | DESC],[字段名2 [ASC |DESC]]...;
查询出的数据首先按照字段1进行分组排序,再将字段1相同的结果按照字段2进行分组排序,依此类推。
回溯统计可以简单地理解为在根据指定字段分组后,系统又自动对分组的字段向上进行了一次新的统计并产生一个新的统计数据,且该数据对应的分组字段值为 NULL。
回溯统计的实现很简单,只需要在 GROUP BY 字段
后添加 WITH ROLLUP
即可。
SELECT [select 选项] 字段列表 FROM 数据表名 [WHERE 条件表达式] GROUP BY 字段名1 [ASC |DESC],[字段名2 [ASC | DESC]] ... WITH ROLLUP;
下面查看 sh_goods 表中每个分类 category_id 下的商品数量为例演示回溯统计的使用。
SELECT category_id, COUNT(*) FROM sh_goods GROUP BY category_id WITH ROLLUP;
+-------------+---------+
| category_id | COUNT(*)|
+-------------+---------+
| 3 | 3 |
| 6 | 1 |
| NUll | 4 |
+-------------+---------+
从上述执行结果可知,在获取每种商品分类 category_id 下的商品数量后,系统又自动对获取的数量进行了一次累加统计,并且此累加的新数据(如4)对应的分组字段(如category_id)的值为NULL。此行的记录就是对category_id分组的一次回溯统计。
在了解了单字段的分组回溯统计后,演示如何在MySQL中对多分组进行回溯统计。
SELECT score, comment_count, COUNT(*) FROM sh_goods GROUP BY score, comment_count WITH ROLLUP;
+-------+---------------+----------+
| score | comment_count | COUNT(*) |
+-------+---------------+----------+
| 2.5 | 200 | 1 |
| 2.5 | NUll | 1 |
| 3.9 | 500 | 2 |
| 3.9 | NUll | 2 |
| NUll | NUll | 3 |
+-------+---------------+----------+
在上述SQL语句中,分组操作根据 GROUP BY 后的字段从前往后依次执行,即先按 score 分组,然后再按 comment_count 分组;
数据分组后系统再进行回溯统计,它与分组的操作正好相反,从 GROUP BY 后最后一个指定的分组字段开始回溯统计,并将结果上报,然后根据上报结果依次向前一个分组的字段进行回溯统计,即先回溯统计 comment_count 分组的结果,再根据 comment_count 的回溯结果对 score 分组进行回溯统计。
从执行结果可看出,score 值相同的情况下,按 comment_count 分组后,实现了 2 次回溯统计,对应的分组字段 comment_count 含有 2 个 NULL 值,统计结果分别进行了累加;接着按上次的结果对score 分组字段进行了1次回溯统计,对应的分组字段 score 中含有一个NULL值,然后又对上一次的统计结果再次进行累加。
虽然回溯统计对数据的分析很有帮助,但是MySQL的同一个查询语句中回溯统计(WITH ROLLUP) 与排序(ORDER BY) 仅能出现一个。
当对查询的数据执行分组操作时,可以利用 HAVING 根据条件进行数据筛选,与WHERE功能相同,但是在实际运用时两者有一定的区别。
WHERE 操作是从数据表中获取数据,将数据从磁盘存储到内存中,而 HAVING 是对已存放到内存中的数据进行操作。
HAVING 位于GROUP BY 子句后,而 WHERE 位于 GROUP BY 子句之前。
HAVING 关键字后可以使用聚合函数,而 WHERE 则不可以。通常情况下,HAVING 关键字与GROUP BY 一起使用,对分组后的结果进行过滤。
SELECT [select选项] 字段列表 FROM 数据表名 [WHERE 条件表达式] GROUP BY 字段名 [ASC | DESC], ...[WITH ROLLUP] HAVING 条件表达式;
WHERE 条件之后的所有语句都是对内存中的数据进行操作。而 HAVING 则根据条件表达式对分组后的内容进行过滤。
在数据库操作中,数据的 SELECT、UPDATE 和 DELETE 等操作都可以使用条件表达式,用于获取、更新 或 删除 给定条件的数据。
算术运算符适用于数值类型的数据,通常应用在 SELECT 查询结果的字段中,在 WHERE 条件表达式中应用较少。
运算符 | 描 述 | 运算符 | 描 述 |
---|---|---|---|
+ | 加运算 | / | 除运算 |
- | 减运算 | % | 取模运算 |
* | 乘运算 |
运算符两端的数据可以是真实的数据(如5),或数据表中的字段(如price),而参与运算的数据一般称之为操作数,操作数与运算符组合在一起统称为表达式(如5+2)。
在MySQL中可以直接利用SELECT查看数据的运算结果。
无符号的加减乘法运算
在MySQL中,若运算符 +
-
和 *
的操作数都是无符号整型,则运算结果也是无符号整型。
例如,商品表 sh_goods 中的 id 字段就是无符号整型,下面对 sh_goods 表中前2条记录的 id 进行加1、减1 以及 乘2 操作。
SELECT id, id+1, id-1, id*2 FROM sh_goods LIMIT ;
+----+------+------+------+
| id | id+1 | id-1 | id*2 |
+----+------+------+------+
| 1 | 2 | 0 | 2 |
| 2 | 3 | 1 | 4 |
+----+------+------+------+
有符号的减法运算结果
MySQL中,默认情况下运算符 -
的操作数若都为无符号整型,则结果一定是无符号的整型、若操作数的差值为负数,那么系统就会报错。
例如,对 sh_ goods 表中前2条记录的id执行减3操作。
SELECT id-3 FROM sh_goods LIMIT 2;
ERROR 1690 (22003): BIGINI UNSIGNED value is out of range in '('shop'.'sh_goods'.'id' -3)'
从上述执行结果可知,减法运算的结果已经超出了无符号 BIGINT 的最大范围。因此,无论运算符 -
的操作数是否含有符号,若要获得一个有符号的运算结果,可以使用 CAST(... AS SIGNED)
将无符号整型 id 强制转换为有符号的整型数据。
SELECT CAST(id AS SIGNED)-3 FROM sh_goods LIMIT 2;
+----------------------+
| CAST(id AS SIGNED)-3 |
+----------------------+
| -2 |
| -1 |
+----------------------+
上述执行结果,-2
和 -1
中的符号 -
,表示负号,它是一个一元操作符,仅有一个操作数,如2。而运算符 -
表示减法运算时,是一个二元操作符,有两个操作数,如 id-3
中的 id
和3
。
含有精度的运算
算术运算除了可以对整数运算外,还可以对浮点数进行运算。
在对浮点数进行加减运算时,运算结果中的精度(小数点后的位数)等于参与运算的操作数的最大精度。
例如 1.2+1.400,1.400 的精度最大为 3,则运算结果的精度就为 3;在对浮点数进行乘法运算时,运算结果中的精度,以参与运算的操作数的精度和为准。例如 1.2*1.400,1.2 的精度为 1,1.400 的精度为3,则运算结果中的精度就为4(1+3)。
/ 运算
运算符 /
在MySQL中用于除法操作,且运算结果使用浮点数表示,浮点数的精度等于被除数( /
运算符左侧的操作数) 的精度加上系统变量 div_precision_increment 设置的除法精度增长值,读者可通过以下SQL语句查找其默认值。
SHOW VARIABLES LIKE 'div_precision_increment'
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| div_precision_increment | 4 |
+-------------------------+-------+
NULL参与算术运算
在算术运算中,NULL是一个特殊的值,它参与的算术运算结果均为NULL。
DIV与MOD运算符
在MySQL中,运算符DIV
与 /
都能实现除法运算,区别在于前者的除法运算结果会去掉小数部分,只返回整数部分。
MySQL中的运算符 MOD
与 %
功能相同,都用于取模运算。
❗取模运算结果的正负与被模数(%左边的操作数)的符号相同,与模数(%右边的操作数)符号无关。
MySQL中还提供了很多进行数学运算的函数,常用的数学函数如下。
运 算 符 | 描 述 |
---|---|
CEIL(x) | 返回大于等于 x 的最小整数 |
FLOOR(x) | 返回小于等于 x 的最大整数 |
FORMAT(x,y) | 返回小数点后保留 y 位的 x(进行四舍五入) |
ROUND(x [,y]) | 计算离x最近的整数;若设置参数y,与FORMAT(x,y)功能相同 |
TRUNCATE(x,y) | 返回小数点后保留 y 位的 x(舍弃多余小数位,不进行四舍五入) |
ABS(x) | 获取x的绝对值 |
MOD(x,y) | 求模运算,与x%y的功能相同 |
PI() | 计算圆周率 |
SQRT(×) | 求x的平方根 |
POW(x,y) | 幂运算函数,计算x的y次方,与POWER(x,y)功能相同 |
RAND() | 默认返回 0 到 1 之间的随机数,包括0和1 |
RAND() 函数用于返回 0 到 1 之间的随机数,若要获取指定区间 (min≤num
FLOOR(min+RAND()*(max-min))
获取。
若要获取相同的随机数,可以为 RAND()函数添加整数参数。
RAND() 函数还可与 ORDER BY 结合使用,用于随机获取指定的数据。
例如,根据商品分类进行分组,然后使用 ORDER BY RAND()
实现分组后记录的随机排序。
比较运算符是MySQL常用运算符之一,通常应用在条件表达式中对结果进行限定。
MySQL中比较运算符的结果值有3种,分别为1(TRUE,表示为真)、0(FALSE,表示为假) 或 NULL。
运 算 符 | 描 述 |
---|---|
= | 用于相等比较 |
<=> | 可以进行 NULL 值比较的相等运算符 |
> | 表示大于比较 |
< | 表示小于比较 |
>= | 表示大于等于比较 |
<= | 表示小于等于比较 |
<>、!= | 表示不等于比较 |
BETWEEN … AND … | 比较一个数据是否在指定的闭区间范围内,若在则返回1,若不在则返回0 |
NOT BETWEEN … AND … | 比较一个数据是否不在指定的闭区间范围内,若不在则返回1,若在则返回0 |
IS | 比较一个数据是否是TRUE、FALSE 或 UNKNOWN,若是则返回1,否则返回0 |
IS NOT | 比较一个数据是否不是TRUE、FALSE 或 UNKNOWN,若不是则返回1,否则返回0 |
IS NULL | 比较一个数据是否是NULL,若是则返回1,否则返回0 |
IS NOT NULL | 比较一个数据是否不是NULL,若不是则返回1,否则返回0 |
LIKE ‘匹配模式’ | 获取匹配到的数据 |
NOT LIKE ‘匹配模式’ | 获取匹配不到的数据 |
所有运算符都可以对数字和字符串进行比较,若参与比较的操作数的数据类型不同,则MySQL会自动将其转换为同类型的数据后再进行比较。
MySQL中比较运算符 =、>、<、>=、<=、<>、!= 在与NULL进行比较时,结果均为NULL。
在MySQL中运算符 =
与 <=>
均可以用于比较数据是否相等,两者的区别在于后者可以对 NULL 值进行比较。
在条件表达式中若需要对指定区间的数据进行判断时,可使用 BETWEEN…AND…
实现。
BETWEEN 条件1 AND 条件2
条件1 到 条件2 之间的范围(包含条件1和条件2),并且在设置时,条件1必须小于等于条件2。
MySQL中查询数据时,除了可以使用 LIKE 实现模糊查询,还可以利用 REGEXP 关键字指定正则匹配模式轻松完成更为复杂的查询。
其中,正则表达式的语法与其他编程语言相同,可自行查看相关的资料进行学习,这里不再赘述。
例如,获取 sh_goods 表中描述字段内含有 人
或 必备
词语的商品 id、name 和 content 字段内容。
SELECT id, name, content, FROM sh_goods WHERE content REGEXP '人|必备';
在上述SQL语句中,REGEXP 关键字用于标识正则匹配,人 | 必备
为正则匹配模式。其中,符号 |
在正则中表示分隔符,用于分隔多种条件,在匹配时只要指定字段满足分隔符左右两边条件中的一个,就表示匹配成功。
除了比较运算符外,MySQL中还提供了很多进行比较运算的函数,常用的比较函数如下。
函 数 | 描 述 |
---|---|
IN() | 比较一个值是否在一组给定的集合内 |
NOT IN() | 比较一个值是否不在一组给定的集合内 |
GREATEST() | 返回最大的一个参数值,至少两个参数 |
LEAST() | 返回最小的一个参数值,至少两个参数 |
IS NULL() | 测试参数是否为空 |
COALESCE() | 返回第一个非空参数 |
INTERVAL() | 返回小于第一个参数的参数索引 |
STRCMP() | 比较两个字符串 |
函数 GREATEST() 和 LEAST() 的参数至少有两个,用于比较后返回一个最大或最小的参数值。
IN() 只要比较的字段或数据在给定的集合内,那么比较结果就为真,NOT IN() 正好与 IN() 的功能相反。
逻辑运算符也是MySQL常用运算符之一,通常应用在条件表达式中的逻辑判断,与比较运算符结合使用。
参与逻辑运算的操作数以及逻辑判断的结果只有3种,分别为1(TRUE,表示为真)、0(FALSE,表示为假)或NULL。
运 算 符 | 描 述 |
---|---|
AND 或 && | 逻辑与。操作数全部为真,则结果为1,否则为0 |
OR 或 II | 逻辑或。操作数中只要有一个为真,则结果为1,否则为0 |
NOT 或 ! | 逻辑非。操作数为0,则结果为1,操作数为1,则结果为0 |
XOR | 逻辑异或。操作数一个为真,一个为假,则结果为1;若操作数全部为真或全部为假,则结果为0 |
仅有逻辑非(NOT或!)是一元运算符,其余均为二元运算符。另外,NOT
和 !
虽然功能相同,但是在一个表达式中同时出现时,先运算 !
,再运算 NOT
。
逻辑与
例,查询 sh_goods 表中关键字为 “电子产品” 的5星商品
SELECT id, name, price FROM sh_goods WHERE keyword = '电子产品' && score = 5;
若使用“&&”连接多个相等比较的条件时,可以使用(a,b)= (x,y) 的方式简化条件表达式 (a=x&&b=y)的书写。
SELECT id, name, price FROM sh_goods WHERE (keyword, score) = ('电子产品', 5);
❗在进行逻辑与操作时,若操作数中含有NULL,而另一个操作数若为1(真),则结果为 NULL;若另一个操作数为0(假),则结果为0。
逻辑或
❗在进行逻辑或操作时,若操作数中含有NULL,而另一个操作数若为1(真),则结果为1;若另一个操作数为0(假),则结果为NULL。
逻辑非
❗逻辑非的操作数仅有一个,当操作数为0(假)时,则运算结果为1;当操作数为1(真)时,则运算结果为0;当操作数为NULL时,运算结果为NULL。
逻辑异或
❗逻辑异或操作,表示两个操作数同时都为1或0,则结果为0;若两个操作数一个为1,一个为0,则结果为1;当操作数为NULL时,则结果为NULL。
MySQL中 =
是一个比较特殊的运算符,既可以用于比较数据是否相等,又可以表示赋值。
因此,MySQL为了避免系统分不清楚运算符 =
表示赋值还是比较的含义,特意增加一个符号 :=
用于表示赋值运算。
在MySQL中,INSERT…SET 和 UPDATE…SET 中出现的运算符 =
都会被认为是赋值运算符。因此,建议除此之外的其他情况,若需要赋值运算符,推荐使用 :=
,如为变量赋值。
位运算符是针对二进制数的每一位进行运算的符号,运算的结果类型为 BIGINT,最大范围可以是64位。
运 算 符 | 描 述 | 示 例 |
---|---|---|
& | 按位与 | SELECT b’1001’ & b’1011’;结果为 9 |
| | 按位或 | SELECT b’1001’ | b’1011’;结果为 11 |
^ | 按位异或 | SELECT b’1001’ ^ b’1011’;结果为 2 |
<< | 按位左移 | SELECT b’1001’ <<2;结果为 36 |
>> | 按位右移 | SELECTb’1001’ >>2;结果为 2 |
~ | 按位取反 | SELECT~b’1001’ & b’1011’;结果为2 |
MySQL 5.7 中参与位运算的数据只能是 BIGINT 类型(64位的整数),而在 MySQL 8.0 中则允许二进制字符串类型的参数,如 BINARY、VARBINARY 和 BLOB。因此,MySQL5.7中二进制类型字段的位运算可能在MySQL8.0中产生不同结果,系统也会报相关的警告信息。
除了位运算符外,MySQL中还提供了进行位运算的函数,常用的运算相关函数如下。
函 数 | 描 述 |
---|---|
BIT_COUNT(N) | 返回在参数N中设置的比特位(二进制位为1)的数量 |
BIT_AND() | 按位返回与的结果 |
BIT_OR() | 按位返回或的结果 |
BIT_XOR() | 按位返回异或的结果 |
运算符优先级可以理解为运算符在一个表达式中参与运算的先后顺序,优先级别越高,则越早参与运算;优先级别越低,则越晚参与运算。
运算符(从高到低) |
---|
INTERVAL |
BINARY, COLLATE |
! |
-(一元,负号)、~(一元,按位取反) |
^ |
*、/、DIV、%、MOD |
-(相减运算符号)、+ |
<<、>> |
& |
| |
=(比较运算符)、<=>、>=、>、<=、<、<>、!=、IS、LIKE、REGEXP、IN |
BETWEEN、CASE、WHEN、THEN、ELSE |
NOT |
AND、&& |
XOR |
OR、|| |
=(赋值运算符)、:= |
同行的运算符具有相同的优先级,除赋值运算符从右到左运算外,其余相同级别的运算符,在同一个表达式中出现时,运算的顺序为从左到右依次进行。
若要提升运算符的优先级别,可以使用圆括号 ()
,当表达式中同时出现多个圆括号时,最内层的圆括号中的表达式优先级最高。
联合查询是多表查询的一种方式,在保证多个SELECT语句
的查询字段数相同
的情况下,合并多个查询的结果
。
SELEC ... UNION [ALL | DISTINCT] SELECT ... [UNION [ALL | DISTINCT] SELECT ...];
UNION
是实现联合查询的关键字,ALL
和 DISTINCT
是联合查询的选项。
其中,ALL
表示保留所有
的查询结果;DISTINCT
是默认值,可以省略
,表示去除完全重复的记录。
SELECT 查询的字段个数
必须相同
,且联合查询的结果
中只保留第一个
SELEC语句对应的字段名称
,即使 UNION 后 SELECT 查询的字段与第一个 SELECT 查询的字段表达含义或数据类型不同,MySQL也仅会根据查询字段出现的顺序,对结果
进行合并
。
若要对联合查询的记录进行排序等操作,需要使用圆括号 ()
包裹每一个
SELECT语句,在 SELECT语句内 或 在联合查询的最后添加 ORDER BY 语句
。并且若要排序生效
,必须在 ORDER BY 后添加LIMIT 限定
联合查询排序的数量,通常推荐使用大于表记录数
的任意值
。
交叉连接返回的结果是被连接的两个表中所有
数据行的笛卡儿积
。
SELECT 查询字段 FROM 表1 CROSS JOIN 表2;
CROSS JOIN 用于连接两个要查询的表,通过该语句可以查询两个表中所有的数据组合。
内连接是一种常见的连接查询,它根据匹配条件
返回第1个表
与第2个表
所有匹配成功
的记录。
SELECT 查询字段 FROM 表1 [INNER] JOIN 表2 ON 匹配条件;
ON
用于指定内连接的查询条件,在不设置ON
时,与交叉连接
等价
。此时,可以使用 WHERE
完成条件的限定,效果与ON
一样。但由于 WHERE 是限定已全部查询
出来的记录,那么在数据量很大的情况下,此操作会浪费很多性能。
自然连接
查询是内连接中的一种特殊查询。它是指相互连接的表在物理上
为同一个表
,但逻辑上
分为两个表
。
左外连接是外连接查询中的一种,也可以将其称为左连接。它用于返回连接关键字
左表
中所有的记录
,以及右表
中符合连接条件
的记录。当左表的某行记录在右表中没有匹配
的记录时,右表中相关的记录将设为NULL
。
SELECT 查询字段 FROM 表1 LEFT [OUTER] JOIN 表2 ON 匹配条件;
关键字 LEFT [OUTER] JOIN
左边的表(表1)被称为左表,也可称为主表
;关键字右边的表(表2)被称为右表,也可称为从表
。其中,OUTER
在查询时可以省略
。
右外连接也是外连接查询中的一种,可以将其称为右连接。它用于 返回连接关键字右表(主表)
中所有
的记录,以及左表(从表)
中符合连接条件
的记录。当右表的某行记录在左表中没有匹配
的记录时,左表中相关的记录将设为NULL
。
SELECT 查询字段 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 匹配条件;
外连接是最常用
的一种查询数据的方式,分左外
连接和右外
连接。它与内连接的区别是,内连接只能获取符合连接条件
的记录,而外连接不仅可以获取符合连接条件的记录,还可以保留主表与从表不能匹配
的记录。
在连接查询时,若数据表连接的字段同名
,则连接时的匹配条件
可以使用 USING
代替ON。
SELECT 查询字段 FROM 表1 [CROSS | INNER | LEFT | RIGHT] JOIN 表2 USING(同名的联结字段);
多个同名
的连接字段
之间使用逗号
分隔。
USING关键字在实际开发
中并不常使用
,原因在于设计表的时候不能确定使用相同的字段名称保存对应的数据。
子查询可以理解为,在一个SQL语句A(SELECT、INSERT、UPDATE等)中嵌入
一个查询语句B,作为执行的条件或查询的数据源(代替FROM后的数据表)
,那么B就是子查询语句,它是一条完整的SELECT 语句,能够独立地执行
。
在含有子查询的语句中,子查询必须书写在圆括号
内。SQL语句首先会执行子查询中的语句,然后再将返回的结果作为外层SQL语句的过滤条件,当遇到同一个SQL语句中含有多层子查询时,它们执行的顺序是从最里层
的子查询开始执行。
子查询的划分方式有多种,最常见的是以功能和位置
进行划分。按子查询的功能可以分为标量子查询
、列子查询
、行子查询
和表子查询
。
按子查询出现的位置
可以分为 WHERE子查询
和 FROM子查询
。
标量子查询
、列子查询
和 行子查询
都属于WHERE子查询
,而表子查询
属于FROM子查询
。
标量子查询指的是子查询返回的结果是一个数据,即一行一列
。
WHERE 条件判断 {= | <>} (SELECT 字段名 FROM 数据源 [WHERE] [GROUP BY] [HAVING] [ORDER BY][LIMIT]);
标量子查询利用比较运算符 =
或 <>
,判断子查询语句返回的数据是否与指定的条件相等或不等,然后根据比较结果完成相关需求的操作。其中,数据源表示一个符合二维表结构的数据,如数据表。
列子查询指的是子查询返回的结果是一个字段符合条件的所有数据,即一列多行
。
WHERE 条件判断 {IN | NOT IN} (SELECT 字段名 FROM 数据源 [WHERE] [GROUP BY] [HAVING] [ORDER BY] [LIMIT]);
列子查询利用比较运算函数 IN()
或 NOTIN()
,判断指定的条件是否在子查询语句返回的结果集中,然后根据比较结果完成相关需求的操作。
当子查询的结果
是一条包含多个字段
的记录(一行多列)时,称为行子查询。
WHERE (指定字段名1, 指定字段名2, ...) =
(SELECT 字段名1, 字段名2, ... FROM 数据源 [WHERE] [GROUP BY] [HAVING] [ORDER BY] [LIMIT]);
行子查询返回的一条记录与指定
的条件
进行比较
,比较的运算符通常使用 =
,表示子查询的结果必须全部与指定的字段相等
才满足WHERE指定的条件。除此之外,还可以使用MySQL中其他的比较运算符,如 <>
>
等,它们表示的含义如下表。
不同运算符的行比较 | 描 述 |
---|---|
(a, b) = (x, y) | 表达的含义等价于 (a=x)AND(b=y) |
(a, b) < = > (x, y) | 表达的含义等价于 (a< = > x)AND(b< = >y) |
(a, b) < > (x, y) | 表达的含义等价于 (a< >x)OR(b < > y) |
(a, b) > (x, y) | 表达的含义等价于 (a>x) OR ((a=x)AND(b>y)) |
(a, b) >= (x, y) | 表达的含义等价于 (a>x) OR ((a=x)AND(b>=y)) |
(a, b) < (x, y) | 表达的含义等价于 (a |
(a, b) <= (x, y) | 表达的含义等价于 (a |
从表中可知,行
在相等比较(=或<=>)
时,各条件之间是与
的逻辑关系;在不等
比较(<>或!)时,各条件之间是或
的逻辑关系;在进行其他
方式比较时,各条件之间的逻辑关系包含两种情况
。
表子查询指的是子查询的返回结果用于FROM数据源
,它是一个符合二维表结构的数据,可以是一行一列
、一列多行
、一行多列
或 多行多列
。
SELECT 字段列表 FROM (SELECT 语句) [AS] 别名 [WHERE] [GROUP BY] [HAVING] [ORDER BY] [LIMIT];
FROM 后的数据源都是表名。因此,当数据源是子查询
时必须
为其设置别名
,同时也是为了将查询结果作为一个表使用时,可以进行条件判断、分组、排序以及限量等操作。
在 WHERE 子查询中,不仅可以使用比较运算符,还可以使用MySQL提供的一些特定关键字,如IN
。除此之外,常用的子查询关键字还有 EXISTS
、ANY
和 ALL
。
带EXISTS关键字的子查询
带 EXISTS 关键字的子查询返回的结果只有0
和1
两个值。其中,0代表不成立
,1代表成立
。
WHERE EXISTS (子查询语句);
EXISTS关键字用于判断子查询语句是否有返回的结果,若存在则返回1,否则返回0。
带ANY关键字的子查询
ANY关键字的子查询时,表示给定的判断条件,只要符合ANY子查询结果中的任意一个
,就返回1,否则返回0。
WHERE 表达式 比较运算符 ANY (子查询语句);
当比较运算符为 =
时,其执行的效果等价于 IN
关键字。
MySQL中还有一个关键字SOME,与ANY的功能完全相同。MySQL在设计时添加SOME的原因在于,英文语法中虽然SOME和ANY的语法含义相同,但是NOTANY和NOTSOME的含义区别很大。前者表示一点也不,相当于NOTALL,而后者仅用于否定部分内容。因此,为了便于以英文为母语的开发者理解,设计出了带SOME和ANY关键字的子查询。
带ALL关键字的子查询
ALL关键字的子查询时,表示给定的判断条件
只有全部符合
ALL子查询的结果时,才返回1,否则返回0。
WHERE 表达式 比较运算符 ALL(子查询语句);
带 ANY
、SOME
或 ALL
关键字的子查询,不能使用 <=>
比较运算符。另外,若子查询结果与条件匹配时有NULL,那么此条记录不参与匹配。
在数据库设计时,为了保证不同表中相同含义数据的一致性和完整性,可为数据表添加外键约束。
例如,员工表中添加了部门表中不存在的部门id,此时就会出现数据信息保存不对等的情况,若在员工表中将部门id设置为外键,即可对相关的操作产生约束。例如,员工表只能插人部门表中含有的记录id。
外键指的是在一个表中引用另一个表中的一列或多列,被引用的列应该具有主键约束
或唯一性约束
,从而保证数据的一致性
和完整性
。其中,被引用的表称为主表;引用外键的表称为从表。
要想在创建数据表或修改数据结构时添加外键约束,在相应的位置添加以下SQL语句即可。
[CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
MySQL可以通过 FOREIGN KEY...REFERENCES
关键字向数据表中添加外键约束。
可选关键字 CONSTRAINT
用于定义外键约束的名称 symbol
,如果省略,MySQL 将会自动生成一个名字。
可选关键字 index_name
,表示外键索引名称,如果省略,MySQL也会在建立外键时自动创建一个外键索引,加快查询速度。
index_col_name, ...
表示从表中外键名称列表;tbl_name
表示主表,主表后的参数列表index_col_name, ...
表示主键约束或唯一性约束字段。
ON DELETE
与 ON UPDATE
用于设置主表中的数据被删除或修改时,从表对应数据的处理办法,其后的各参数具体说明如下表所示。
参数名称 | 功能描述 |
---|---|
RESTRICT | 默认值。拒绝主表删除或修改外键关联字段 |
CASCADE | 主表中删除或更新记录时,同时自动删除或更新从表中对应的记录 |
SET NULL | 主表中删除或更新记录时,使用NULL值替换从表中对应的记录(不适用于 NOT NULL字段) |
NO ACTION | 与默认值RESTRICT相同,拒绝主表删除或修改外键关联字段 |
SET DEFAULT | 设默认值,但InnoDB目前不支持 |
建立外键关系的两个数据表的相关字段数据类型必须相似,也就是要求字段的数据类型可以相互转换。
例如,INT 和 TINYINT 类型的字段可以建立外键关系,而 INT 和 CHAR 类型的字段则不可以建立外键关系。下面在mydb数据库中,以员工表employees和部门表department为例,演示如何在CREATE TABLE和ALTER TABLE时添加外键约束的两种方式。
CREATETABLE时添加外键约束
为从表创建外键约束时,首先要保证数据库中已存在主表,否则程序会报 不能添加外键约束
的错误。
# 1.在mydb数据库下创建主表
CREATE TABLE mydb.department (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '部门编号',
name VARCHAR(50) NOT NULL COMMENT'部门名称'
) DEFAULT CHARSET=utf8;
# 2.在mydb数据库下创建从表,添加外键约束
CREATE TABLE mydb.employees (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '员工编号',
name VARCHAR(120) NOT NULL COMMENT '员工姓名',
dept_id INT UNSIGNED NOT NULL COMMENT '部门分类编号',
CONSTRAINT FK_ID FOREIGN KEY(dept_id) REFERENCES department (id) ON DELETE RESTRICT ON UPDATE CASCADE
) DEFAULT CHARSET=utf8;
在上述SQL语句中,为 employees 表中的 dept_id 字段添加外键约束,与主表 department 中的主键 id相关联。
同时,利用 ON DELETE 指定从表此关联字段含有数据时,拒绝主表 department 执行删除操作,利用 ON UPDATE 设置主表 department 执行更新操作时,从表 employees 中的相关字段也执行更新操作。
❗定义外键约束名称(如FK_ID)时,不能加单引号和双引号。
ALTER TABLE时添加外键约束
对于已经创建的数据表,则可以通过 ALTER TABLE 的方式添加外键约束。
例如,若mydb数据库中已有两个数据表department和employees,employees表在创建时未添加外键约束,此时就可以通过以下的 ALTER TABLE 方式实现。
ALTER TABLE mydb.employees
ADD CONSTRAINT FK_ID FOREIGN KEY (dept_id) REFERENCES department (id)
ON DELETE RESTRICT ON UPDATE CASCADE;
在添加完外键约束后,可以利用 DESC 查看数据表 employees 中添加了外键约束的字段信息。
DESC mydb.employees dept_id;
+---------+------------------+------+-----+---------+-------+
| Field | Type | NULL | Key | Default | Extra |
+---------+------------------+------+-----+---------+-------+
| dept_id | int(10) unsigned | NO | MUL | NULL | |
+---------+------------------+------+-----+---------+-------+
添加了外键约束的 dept_id 字段的 Key(索引) 值为MUL,表示非唯一性索引(MUL TIPLE KEY),值可以重复。由此可见,在创建外键约束时,MySQL会自动为没有索引的外键字段创建索引。
在MySQL中使用 SHOW CREATE TABLE
查看 employees 表的详细结构。
SHOW CREATE TABLE mydb.employees\G
*************1.row*************
Table: employees
Create Table: CREATE TABLE 'employees' (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '员工编号',
`name` varchar(120) NOT NULL COMMENT '员工姓名',
`dept_id` int(10) unsigned NOT NULL COMMENT '商品分类编号',
PRIMARY KEY (`id`),
KEY `FK_ID` (`dept_id`),
CONSTRAINT FK_ID FOREIGN KEY (`dept_id`) REFERENCES `department` (`id`) ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
在上述结果中,为 dept_id 字段添加外键约束后,当 dept_id 没有索引时,服务器就会自动为其创建与外键同名的索引。数据表的默认存储引擎为 InnoDB。
由于 ON DELETE RESTRICT 设置的拒绝主表的删除操作属于默认值,因此显示表创建语句时省略了此设置。若设置为删除主表记录,则从表对应字段设置为NULL,即可在表的详细结构中显示到 ON DELETE SETNULL的设置。
实体之间具有一对一、一对多和多对多的联系,而具有关联的表中的数据,可以通过连接查询的方式获取,并且在没有添加外键约束时,关联表中的数据插入、更新和删除操作互不影响。但是对于添加了外键约束的关联表而言,数据的插入、更新和删除操作就会受到一定的约束。
添加数据
一个具有外键约束的从表在插入数据时,外键字段的值会受主表数据的约束,保证从表插人的数据必须符合约束规范的要求。如从表外键字段不能插入主表中不存在的数据。
更新数据
对于建立外键约束的关联数据表来说,若对主表进行更新操作,从表将按照其建立外键约束时设置的ON UPDATE 参数自动执行相应的操作。例如,当参数设置为 CASCADE 时,如果主表发生更新,则从表也会对相应的字段进行更新。
外键约束在使用时既有一定的优势,同时又会带来一定的问题,具体如下。
✔️优势
外键可节省开发量。
外键能约束数据有效性,防止非法数据的插入。
❌劣势。
使用外键约束,会带来额外的开销。
主表被锁定时,会引发从表也被锁定。
删除主表的数据时,需先删除从表的数据。
含有外键约束的从表字段不能修改表结构。
删除数据
对于已建立外键约束的关联数据表来说,若要对主表执行删除操作,从表将按照其建立外键约束时设置的 ON DELETE 参数自动执行相应的操作。例如,当参数设置为 RESTRICT 时,如果主表进行删除操作,同时从表中的外键字段有关联记录,就会阻止主表的删除操作。
关联表在删除操作时体用 DISTRICT 严格模式,主表中每条记录的删除,都要保证从表中没有相关记录的对应数据,这会对开发造成很大的不便。因此,对于添加外键约束的 ON DELETE 一般都使用SET NULL模式,即删除主表记录时,将从表中对应的记录设置为NULL,同时要保证从表中对应的外键字段允许为空,否则不允许设置该模式。
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
下面以解除员工表 employees 与部门表 department 之间的外键约束为例进行演示。
ALTER TABLE mydb.employees DROP FOREIGN KEY FK_ID;
在删除employees表的外键约束后,下面利用 DESC 查询 employees 表中删除了外键约束的字段信息。
DESC mydb.employees dept_id;
+---------+------------------+------+-----+---------+-------+
| Field | Type | NULL | Key | Default | Extra |
+---------+------------------+------+-----+---------+-------+
| dept_id | int(10) unsigned | NO | MUL | NULL | |
+---------+------------------+------+-----+---------+-------+
从上述执行结果可知,在删除 dept_id 的外键约束后,dept_id 的 Key 值依然为 MUL,这是由于删除外键约束时并不会自动删除系统创建的普通索引,此时可以通过 SHOW CREATE TABLE
查看。
SHOW CREATE TABLE mydb.employees\G
*************1.row*************
Table: employees
Create Table: CREATE TABLE 'employees' (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '员工编号',
`name` varchar(120) NOT NULL COMMENT '员工姓名',
`dept_id` int(10) unsigned NOT NULL COMMENT '商品分类编号',
PRIMARY KEY (`id`),
KEY `FK_ID` (`dept_id`),
CONSTRAINT FK_ID FOREIGN KEY (`dept_id`) REFERENCES `department` (`id`) ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
若要在删除外键约束后,同时删除系统为外键创建的普通索引,则需要通过手动删除索引的方式完成。
ALTER TABLE mydb.employees DROP KEY FK_ID;
完成上述操作后,可再次利用 DESC 查看已删除的外键字段的 Key 值为空,SHOW CREATE TABLE时表中的外键约束以及普通索引全部已删除。
在正常的工作环境中,为了保证数据库的安全,数据库的管理员会对需要操作数据库的人员分配用户名、密码以及可操作的权限范围,让其仅能在自己权限范围内操作。
用户是数据库的使用者和管理者,MySQL通过用户的设置来控制数据库操作人员的访问与操作范围。
在安装MySQL时,系统会自动安装一个名为 mysql 的数据库,该数据库主要用于维护数据库的用户以及权限的控制和管理。
MySQL中的所有用户信息都保存在 mysql.user 数据表中。
使用 DESC 即可查看 user 表含有的45个字段,下面以表格的形式列举 user 表中的一些常用字段。
字 段 名 | 数 据 类 型 | 默认值 |
---|---|---|
Host | char(60) | |
User | char(60) | |
Select_priv | enum(‘N’, ‘Y’) | N |
Insert_priv | enum(‘N’, ‘Y’) | N |
Update_priv | enum(‘N’, ‘Y’) | N |
Delete_priv | enum(‘N’, ‘Y’) | N |
Create_priv | enum(‘N’, ‘Y’) | N |
Drop_priv | enum(‘N’, ‘Y’) | N |
Reload_priv | enum(‘N’, ‘Y’) | N |
Shutdown_priv | enum(‘N’, ‘Y’) | N |
Process_priv | enum(‘N’, ‘Y’) | N |
File_priv | enum(‘N’, ‘Y’) | N |
Grant_priv | enum(‘N’, ‘Y’) | N |
ssl_tupe | enum(‘’, ‘ANY’, ‘X509’, SPECIFIED) | |
ssl_cipher | blob | NULL |
x509_issure | blob | NULL |
x509_subject | blob | NULL |
max_questions | int(11) unsigned | 0 |
max_updates | int(11) unsigned | 0 |
max_connections | int(11) unsigned | 0 |
max_user_connections | int(11) unsigned | 0 |
plugin | char(64) | mysql_native_password |
authentication_string | text | NULL |
password_expired | enum(‘N’, ‘Y’) | N |
password_last_changed | timestamp | NULL |
password_lifetime | smallint(5) unsigned | NULL |
account_locked | enum(‘N’, ‘Y’) | N |
在表中,根据字段的功能可将其分为6类,分别为客户端访问服务器的账号字段、验证用户身份的字段、安全连接的字段、资源限制的字段、权限字段以及账户是否锁定的字段。
Host 和 User 字段共同组成的复合主键用于区分 MySQL 中的账户,User 字段用于代表用户的名称,Host 字段表示允许访问的客户端 IP 地址或主机地址,当 Host 的值为 *
时,表示所有客户端的用户都可以访问。
下面通过SELECT查询user表中默认用户的Host和User值,具体SQL语句及执行结果如下。
SELECT host, user FROM mysql.user
+-----------+----------------+
| host | user |
+-----------+----------------+
| localhost | mysql.session |
| localhost | mysql.sys |
| localhost | root |
+-----------+----------------+
从上述执行结果可知,在 user 表中,除了默认的 root 超级用户外,在MySQL5.7开始还额外地新增了两个用户 mysql.session 和 mysql.sys。前者用于用户身份验证,后者用于系统模式对象的定义,防止DBA(数据库管理员)重命名或删除root用户时发生错误。
默认情况下,用户 mysql.session 和 mysql.sys 已被锁定,使得数据库操作人员无法使用这两个用户通过客户端连接MySQL服务器。建议不要随意解锁和使用 mysql.session 和 mysql.sys 用户。
在MySQL 5.7中,mysql.user 表中已不再包含 Password 字段,而是使用 plugin 和authentication_string 字段保存用户身份验证的信息。
其中,plugin 字段用于指定用户的验证插件名称,authentication_string 字段是根据 plugin 指定的插件算法对账户明文密码加密后的字符串。
下面通过SELECT 查询 user表中 root 用户默认的 plugin 和 authentication_string 值。
SELECT plugin, authentication_string FROM mysql.user WHERE user = 'root'
+-----------------------+-------------------------------------------+
| plugin | authentication_string |
+-----------------------+-------------------------------------------+
| mysql_native_password | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+-----------------------+-------------------------------------------+
MySQL中 root 用户的默认验证插件名为 mysql_native_password。
authentication_string 字段保存的则是一串不能看出具体含义的值,相对于能够直接看懂的明文密码,它是经过加密处理的暗码。
除此之外,与身份验证的账号密码相关的字段还有 password_expired(密码是否过期)、password_last_changed(密码最后一次修改的时间)以及password_lifetime(密码的有效期)。
在客户端与 MySQL 服务器连接时,除了可以基于账户名以及密码的常规验证外,还可以判断当前连接是否符合SSL安全协议,与其相关的字段有以下几种。
ssl_type:用于保存安全连接的类型,它的可选值有"(空)、ANY(任意类型)、X509(X509证书)、SPECIFIED(规定的)4种。
ssl_cipher:用于保存安全加密连接的特定密码。
x509_issuer:保存由CA签发的有效的X509证书。
x509_subject:保存包含主题的有效的X509证书。
通常标准的发行版MySQL默认未启用SSL加密连接,可以通过以下SQL语句查看。
SHOW VARIABLES LIKE 'have_openssl';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| have_openssl | DISABLED |
+---------------+-----------+
have_openssl 的取值为 DISABLED 表示已禁用加密
在 mysql.user 表中提供的以 max_
开头的字段,保存对用户可使用的服务器资源的限制,用来防止用户登录MySQL服务器后的不法或不合规范的操作浪费服务器的资源,各字段的具体含义如下所示。
max_questions:保存每小时允许用户执行查询操作的最多次数。
max_updates:保存每小时允许用户执行更新操作的最多次数。
max_connections:保存每小时允许用户建立连接的最多次数。
max_user_connections:保存允许单个用户同时建立连接的最多数量。
以上列举的用户资源限制字段默认值均为0,表示对此用户没有任何的资源限制。
在 mysql.user 表中提供的以 _priv
结尾的字段一共有29个,这些字段保存了用户的全局权限,如 Select_priv 查询权限、Insert_priv 插入权限、Update_priv 更新权限等。
其中,user 表对应的权限字段的数据类型都是 ENUM 枚举类型,取值只有N或Y两种。N表示该用户没有对应权限,Y表示该用户有对应权限。为了保证数据库的安全,这些字段的默认值都为N,如果需要可以对其进行修改。
在mysql.user表中提供的 account_locked 字段用于保存当前用户是锁定还是解锁状态。该字段是一个枚举类型,当其值为N时表示解锁,此用户可以用于连接服务器;当其值为Y时表示该用户已被锁定,不能用于连接服务器使用。
由于MySQL中所有用户的信息都保存在 mysql.user 表中,因此,创建用户可以直接利用 root 用户登录MySQL服务器后,向 mysql.user 表中插入记录,但是在开发中为保证数据的安全,并不推荐使用此方式创建用户。而是采用MySQL提供的 CREATE USER
和 GRANT
语句创建用户。其中,GRANT语句在创建用户的同时还可以完成权限的设置。
使用 CREATE USER
语句每创建一个新用户,都会在 mysql.user 表中添加一条记录,同时服务器会自动修改相应的授权表。但需要注意的是,该语句创建的新用户默认情况下没有任何权限,需要使用GRANT进行授权。
CREATE USER [IF NOT EXISTS]
账户名 [用户身份验证选项] [, 账户名 [用户身份验证选项]] ...
[WITH 资源控制选项] [密码管理选项 | 账户锁定选项]
CREATE USER
可以一次创建多个用户,多个用户之间使用逗号分隔。其中,账户名是由 用户名@主机地址
组成的,其余选项在创建用户时,若未设置则使用默认值,具体如下表所示。
选 项 | 默 认 值 |
---|---|
用户身份验证选项 | 由 default_authentication_plugin 系统变量定义的插件进行身份验证 |
加密连接协议选项 | NONE |
资源控制选项 | N(表示无限制) |
密码管理选项 | PASSWORD EXPIRE DEFAULT |
用户锁定选项 | ACCOUNT UNLOCK |
在上表中,用户身份验证选项的设置仅适用于其前面的用户名,可将其理解为某个用户的私有属性;其余的选项对声明中的所有用户都有效,可以将其理解为全局属性。
创建用户时,用户名的设置不能超过32个字符,且区分大小写,但是主机地址不区分大小写。
创建最简单的用户
在创建用户时,若不指定主机地址、密码以及相关的用户选项,则表示此用户在访问 MySQL服务器时,不限定客户端,不需要密码并且没有任何限制。
CREATE USER 'test1';
使用 SELECT查看 mysql 数据库下的 user 表,查看创建的用户是否已添加到 user 表中保存。
SELECT host, user FROM mysql.user
+-----------+----------------+
| host | user |
+-----------+----------------+
| % | text1 |
| localhost | mysql.session |
| localhost | mysql.sys |
| localhost | root |
+-----------+----------------+
在上述执行结果中,host 的值为 %
时表示当前的用户可以在任何主机中连接 MySQL 服务器;当其值为 localhost
时,表示当前的用户只能从本地主机连接 MySQL 服务器。另外,host 的值为空字符串(")同样也可表示匹配所有客户端。
用户名和主机名在设置时,若不包含空格、
-
等特殊字符,则可以省略引号。另外,当创建的用户名称为空字符串(")时,表示创建的是一个匿名用户,即登录MySQL服务器时不需要输入用户名和密码,这种操作会给MySQL服务器带来极大的安全隐患,因此不推荐用户创建并使用匿名用户操作MySQL服务器。
创建含有密码的用户
若要在创建用户的同时完成用户密码的设置,则可以选取用户身份验证选项的 IDENTIFIED BY 关键字。
CREATE USER 'test2'@'localhost' IDENTIFIED BY '123456';
IDENTIFIED BY 后指定的是字符串形式的明文密码。
通过 SELECT 查询存储在 user 表中 test2 用户的密码。
SELECT plugin, authentication_string FROM mysql.user;
+-----------------------+-------------------------------------------+
| plugin | authentication_string |
+-----------------------+-------------------------------------------+
| mysql_native_password | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+-----------------------+-------------------------------------------+
创建用户时设置的明文密码 “123456”,在 use r表中以默认的算法插性 mysql_native_password
将其转换为暗码。
除此之外,在设置用户密码时还可以指定对密码加密的插件,只需将 IDENTIFIED BY 明文密码
修改为指定的选项即可,具体如下表所示。
选 项 | 描 述 |
---|---|
IDENTIFIED WITH 验证插件名 | 使用指定的身份验证插件对空凭证(未设置用户密码)进行加密 |
IDENTIFIED WITH 验证插件名 BY 明文密码 | 利用指定的身份验证插件对明文密码进行加密 |
IDENTIFIED WITH 验证插件名 AS 哈希字符串 | 指定身份验证的插件,并存储哈希加密字符串 |
IDENTIFIED BY PASSWORD 哈希字符串 | 身份验证插件为默认,并存储哈希加密字符串 |
下面利用 IDENTIFIED WITH验证插件名 BY ‘明文密码’ 修改以上创建test2用户的SQL语句。
CREATE USER 'test2'@'localhost'
IDENTIFIED WITH 'mysql_native_password' BY '123456';
在上述SQL语句中,IDENTIFIED WITH关键字指明验证密码的插件名,关键字BY后跟明文的密码。通过上述设置,明文密码 “123456” 将采用指定的插件算法,存储在user表中的对应字段中。
同时创建多个用户
MySQL中创建用户时,还可以一次创建多个。
CREATE USER
'test2'@'localhost' IDENTIFIED BY '333333';
'test3'@'localhost' IDENTIFIED BY '444444';
在一个CREATE USER 语句中可同时创建多个用户,多个用户之间使用逗号分隔,并且在创建每个用户时可以单独为其设置密码,省略用户身份验证选项时,表明此用户在登录服务器时可以免密登录,但为了保证数据安全,不推荐用户这样做。
设置用户可操作资源范围
在创建用户时,可以添加 WITH 直接为用户指定可操作的资源范围,如登录的用户在一小时内可以查询数据的次数等,具体的参数选项如下表所示。
选 项 | 描 述 |
---|---|
MAX_QUERIES_PER_HOUR | 在任何一个小时内,允许此用户执行多少次查询 |
MAX_UPDATES_PER_HOUR | 在任何一个小时内,允许此用户执行多少次更新 |
MAX_CONNECTIONS_PER_HOUR | 在任何一个小时内,允许此用户执行多少次服务器连接 |
MAX_USER_CONNECTIONS | 限制用户同时连接服务器的最大数量 |
在上表中,MAX_USER_CONNECTIONS 选项的值为 0 时,服务器将根据 max_userconnections 系统变量的值确定用户的同时连接数,若此变量值也为0,表示对该用户没有限制。另外MAX_QUERIES_PER_HOUR 选项不会计算从缓存中查询数据的次数。
例如,创建一个名为test5的本地用户,限制其每小时最多可以更新10次。
CREATE USER
'test5'@'localhost' IDENTIFIED BY '555555'
WITH MAX_UPDATES_PER_HOUR 10;
创建完成后,可以查看 user 表中的 max_updates 字段。
SELECT max_updates FROM user WHERE user='test5';
+-------------+
| max_updates |
+-------------+
| 10 |
+-------------+
设置有密码期限的用户
在创建用户时,不仅可以为用户设置密码,还可以为密码设置有效时间,具体参数如下表所示。
选 项 | 描 述 |
---|---|
PASSWORD EXPIRE | 将密码标记为过期 |
PASSWORD EXPIRE DEFAULT | 根据 default_password_lifetime 系统变量的指定设置密码的有效性 |
PASSWORD EXPIRE NEVER | 密码永不过期 |
PASSWORD EXPIRE INTERVAL n DAY | 将账户密码生存期设置为n天 |
PASSWORD EXPIRE 选项表示在创建用户时含有此选项的用户在登录后,执行任何SQL语句操作前,都需要重置用户的密码,否则会给出一个 “在执行此语句之前,必须使用 ALTER USER 语句重置密码” 的错误提示信息。
在重置用户密码时,操作的用户必须要有全局的 CREATE USER 权限或 mysql 数据库的 UPDATE 特权。
下面创建一个名为 test6 的用户,并设置其密码每 180 天更改一次。
CREATE USER 'test6'@'localhost' IDENTIFIED BY '666666'
PASSWORD EXPIRE INTERVAL 180 DAY;
为了确保MySQL客户端用户本身的安全,通常情况下推荐每3~6个月变更一次数据库用户密码。
设置用户是否锁定
利用 ACCOUNT 关键字可以为创建的用户设置是否被锁定,它有两个可选值,分别为 LOCK(锁定)和UNLOCK(解锁)。被锁定的用户不能在客户端登录MySQL服务器。
下面创建一个锁定的用户test 7。具体SQL语句及执行结果如下。
CREATE USER 'test7'@'localhost' IDENTIFIED BY '777777'
PASSWORD EXPIRE ACCOUNT LOCK;
此时,利用 SELECT 查看 mysql.user 表中名为 test7 的 account_ locked 字段,若其值为Y,则表示当前创建的用户已被锁定。
SELECT account_locked FROM mysql.user WHERE user = 'test7';
此时若利用 test7 在客户端登录MySQL服务器,会报 “账户被锁定,拒绝访问test7@localhost” 用户的提示信息。
在对MySQL中的用户进行管理时,除了创建用户的同时设置密码外,还可为没有密码的用户、密码过期的用户或为指定用户修改密码。
# 第1种语法
ALTER USER 账户名 IDENTIFIED BY '明文密码';
# 第2种语法
SET PASSWORD [FOR 账户名] = '明文密码';
在以上2种语法中,ALTER USER是更改密码的首选SQL语句,推荐使用。第2种语法可能会被记录到服务器的日志或客户端的历史文件中,会有密码泄露的风险,因此建议用户尽量少地使用此方式设置密码。
为指定用户设置密码
mysql.user 表中的 test1 用户是一个可在任何主机中连接MySQL服务器的用户,但此用户在创建时未设置密码,下面将 test1 的密码设置为123456。
ALTER USER 'test1'@'%' IDENTFIED BY '123456';
为登录用户设置密码
若当前通过客户端连接 MySQL 服务器的用户是非匿名用户,则可以使用 USER() 函数更改自己的密码,而无须直接为自己的账户命名。
例如,修改当前正在连接 MySQL 服务器的 root 用户密码,将其设置为000000。
ALTER USER USER() IDENTFIED BY '000000';
USER() 函数获取的是客户端提供的用户和主机地址,它可能与当前通过 MySQL 服务器验证的用户与主机名不同,此时可以利用 MySQL 提供 CURRENT USER() 函数获取。
SELECT CURRENT_USER();
+----------------+
| CURRENT_USER |
+----------------+
| root@localhost |
+----------------+
mysqladmin修改用户密码
在MySQL的安装目录 bin 下还有一个 mysqladmin.exe 应用程序,它通常用于执行一些管理性的工作,以及显示服务器状态等。同时,在 MySQL中也可以使用该命令修改用户的密码。
mysqladmin -u 用户名 [-h主机地址] -p password 新密码
-u 用于指定待要修改的用户名,通常情况下指的是 root 用户;-h 用于指定对应的主机地址,省略时默认值为 localhost;-p 后面的 password 为关键字,而不是待修改用户的原密码。
例如,在命令行窗口中,使用 mysqladmin 命令,将 root 用户的密码修改为 123456。
mysqladmin -u root -p password 123456
Enter password:
在上述命令行中,“-p password” 后指定的是 root 用户修改后的新密码;而 “Enter password" 提示输入的密码指的是 root 用户的旧密码,只有此密码输入正确,才能完成用户密码的修改。
值得一提的是,通过 mysqladmin 完成用户密码的设置时,会有两个安全警告提示。因此,为了确保密码安全应以安全连接方式连接MySQL服务器。在开发中一般不推荐
使用此种方式修改用户密码。
SET PASSWORD 与 PASSWORD() 函数
在前面为用户设置密码的语法中可以看出,SET PASSWORD 在设置密码时,一种是添加PASSWORD() 函数,一种是不添加,两者的区别在于含有 PASSWORD() 函数的密码根据old_passwords 系统变量值 (0 表示 mysql_native_password 插件,2 表示 sha256_password插件)对指定的明文密码加密并验证;而不含有 PASSWORD() 函数的 SET PASSWORD 语句是将明文密码字符串传递给用户的验证插件,并将插件返回的结果保存到mysql.user表的字段中。
root 密码丢失找回
如果忘记MySQL服务的root用户密码,就不能通过以上设置密码的方式找回或重置。此时可以在MySQL的配置文件 my.ini 中添加 skip-grant-tables 选项,然后重启 MySQL 服务后再次利用 root 用户登录,就可以跳过密码的输入直接登录MySQL服务,为 root 用户设置密码。但是此种方式存在非常大的安全风险。因此,建议要慎重使用。
用户创建完成后,管理员可以通过 MySQL 提供的专门SQL语句修改用户的密码、身份验证的方式、资源限制、密码的属性,以及账户的锁定和解锁的状态。
ALTER USER [IF EXISTS]
账户名 [用户身份验证选项], [账户名 [用户身份验证选项]] ...
[WITH 资源限制选项] [密码管理选项 | 账户锁定选项]
ALTER USER 可以同时修改一个或多个用户,多个用户之间使用逗号分隔,并且语法中选项的可选值与创建用户时的选项完全相同。对于每个修改的用户,都会更新其在 mysql.user 表中对应的字段值,而未修改的字段仍然保留它原来的值。
修改验证插件
例如,将 mysql.user 表中名为 test1 的用户验证插件修改为 sha256_password,密码修改为111111,并将密码设置为立刻过期。
ALTER USER test1
IDENTIFIED WITH sha256_password BY '111111'
PASSWORD EXPIRE;
解锁用户
解锁 mysql.user 表中被锁定的用户 test7。
ALTER USER 'test'@'localhost' ACCOUNT UNLOCK;
同时修改多个用户的资源限定
例如,修改 mysql. user 表中的 test1 和 test2 用户,限定单个用户最多可同时建立两个连接,同时将test1 的验证插件修改为MySQL的默认值,test2用户的密码修改为 222222。
ALTER USER
'test1' IDENTFIED WITH mysql_native_password,
'test2'@'localhost' IDENTFIED BY '222222',
WITH max_user_connections 2;
为用户重命名
在利用 ALTER USER 修改用户时只能修改指定账户的相关选项,如密码、验证插件、资源控制选项等,而不能够为用户重新命名,此时可以使用 MySQL 专门提供的RENAME USER语句实现。
RENAME USER 旧用户名1 TO 新用户名1 [, 旧用户名2 TO 新用户名2] ...
RENAME USER 在为用户重命名时,旧用户名与新用户名之间使用 TO 关键字连接,同时为多个用户重命名时使用逗号(,)进行分隔。
DROP USER [IF EXISTS] 账户名 [, 账户名] ...;
在 MySQL5.x 版本之后 DROP USER 语句可以同时删除一个或多个MySQL中的指定用户,并会同时从授权表中删除账户对应的权限行。在 MySQL5.x 之前的版本中,在删除用户前必须先回收用户的权限。其中,账户名与创建用户的格式相同,由 用户名@主机地址
组成。
下面以删除 mysql.user 中test7用户为例进行演示。
DROP USER IF EXISTS test7;
在不添加 IF EXISTS 关键字时,若删除了一个不存在的用户,则该语句的执行会发生错误;在添加后,会在删除不存在的用户时生成一个警告作为提示。其中,在删除账户时,如果省略主机地址,则默认为“%”。
当 DROP USER 语句删除当前正在打开的用户时,则该用户的会话不会被自动关闭。只有在该用户会话关闭后,删除操作才会生效,再次登录将会失败。另外,利用已删除的用户登录服务器创建的数据库或对象不会因此删除操作而失效。
在实际项目开发中,为了保证数据的安全,数据库管理员需要为不同层级的操作人员分配不同的权限,限制登录MySQL服务器的用户只能在其权限范围内操作。同时管理员还可以根据不同的情况为用户增加权限或回收权限,从而控制数据操作人员的权限。
MySQL中的权限信息根据其作用范围,分别存储在mysql数据库中的不同数据表中。当MySQL启动时会自动加载这些权限信息,并将这些权限信息读取到内存中。与权限相关的数据表如下表所示。
数 据 表 | 描 述 |
---|---|
user | 保存用户被授予的全局权限 |
db | 保存用户被授予的数据库权限 |
tables_priv | 保存用户被授予的表权限 |
columns_priv | 保存用户被授予的列权限 |
procs_priv | 保存用户被授予的存储过程权限 |
proxies_priv | 保存用户被授予的代理权限 |
上表中保存权限的数据表,根据权限的操作内容可将权限大致分为数据权限、结构权限以及管理权限(通常只有管理员有管理权限)。
列举MySQL中可以授予和取消的权限,如下所示。
数据权限
权 限 | 权 限 级 别 | 描 述 |
---|---|---|
SELECT | 全局、数据库、表、列 | SELECT |
UPDATE | 全局、数据库、表、列 | UPDATE |
DELETE | 全局、数据库、表 | DELETE |
INSERT | 全局、数据库、表、列 | INSERT |
SHOW DATABASES | 全局 | SHOW DATABASES |
SHOW VIEW | 全局、数据库、表 | SHOW CREATE VIEW |
PROCESS | 全局 | SHOW PROCESSLIST |
结构权限
权 限 | 权 限 级 别 | 描 述 |
---|---|---|
DROP | 全局、数据库、表 | 允许删除数据库、表和视图 |
CREATE | 全局、数据库、表 | 创建数据库、表 |
CREATE ROUTINE | 全局、数据库 | 创建存储过程 |
CREATE TABLESPACE | 全局 | 允许创建、修改或删除表空间和日志文件组 |
CREATE TEMPORARY TABLES | 全局、数据库 | CREATE TEMPORARY TABLE |
CREATE VIEW | 全局、数据库、表 | 允许创建或修改视图 |
ALTER | 全局、数据库、表 | ALTER TABLE |
ALTER ROUTINE | 全局、数据库、存储过程 | 允许删除或修改存储过程 |
INDEX | 全局、数据库、表 | 允许创建或删除索引 |
TRIGGER | 全局、数据库、表 | 允许触发器的所有操作 |
REFERENCES | 全局、数据库、表、列 | 允许创建外键 |
管理权限
权 限 | 权 限 级 别 | 描 述 |
---|---|---|
SUPER | 全局 | 允许使用其他管理操作,如CHANGE MASTER TO等 |
CREATE USER | 全局 | CREATE USER、DROP USER、 RENAME USER 和 REVOKEALL PRIVILEGES |
GRANT OPTION | 全局、数据库、表、存储过程、代理 | 允许授予或删除用户权限 |
RELOAD | 全局 | FLUSH操作 |
PROXY | 与代理的用户权限相同 | |
REPLICATION CLIENT | 全局 | 允许用户访问主服务器或从服务器 |
REPLICATION SLAVE | 全局 | 允许复制从服务器读取的主服务器二进制日志事件 |
SHUTDOWN | 全局 | 允许使用mysqladmin_shutdown |
LOCK TABLES | 全局、数据库 | 允许在有SELECT表权限上使用 LOCK TABLES |
MySQL提供的 GRANT 用于实现用户权限的授予。
GRANT
权限类型 [字段列表] [, 权限类型 [字段列表]] ...
ON [目标类型] 权限级别
TO 账户名 [用户身份验证选项] [, 账户名[用户身份验证选项]] ...
[REQUIRE 连接方式]
[WITH [GRANT OPTION | 资源控制选项}]
权限类型指的就是上表中的权限列,字段列表用于设置列权限,ON 后的目标类型默认为 TABLE,表示将全局、数据库、表或列中的某些权限授予给指定的用户。另外,其值还可以是 FUNCTION(函数) 或 PROCEDURE(存储过程)。
权限级别用于定义全局权限、数据库权限和表权限,添加GRANT OPTION 表示当前账户可以为其他账户进行授权。其余各参数均与 CREATE USER 中的用户选项相同。
下面首先使用 SHOW GRANTS [FOR账户] 查看一下 root 和 test1 用户被授权的情况。
SHOW GRANTS FOR 'root'@'localhost';
+--------------------------------------------------------------------+
|Grants for root@localhost |
+--------------------------------------------------------------------+
|GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION |
|GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION |
+--------------------------------------------------------------------+
SHOW GRANTS FOR 'test1'@'%';
+------------------------------------------+
|Grants for test1@% |
+------------------------------------------+
|GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' |
+------------------------------------------+
在上述SQL语句中,若在客户端使用的是 root 用户登录MySQL服务器,则此处在查看root用户权限时,可以省略 FOR ‘root’@localhost’。
在查询结果中 ALL PRIVILEGES 表示除 GRANT OPTION(授权权限) 和 PROXY(代理权限) 外的所有权限,USAGE 表示没有任何权限。ON后的 *.*
表示全局级别的权限,即MySQL服务器下的所有数据库下的所有表,''@''
表示任何主机中的匿名用户。
在为用户授予权限时,可以分为6个不同的级别,具体的实现在于 ON 子句以及权限列表的不同,如下表所示。
权 限 级 别 | 实 现 语 法 |
---|---|
全局权限 | GRANT 权限列表 ON *.* TO 账户名 [WITH GRANT OPTION]; |
数据库级权限 | GRANT 权限列表 ON 数据库名.* TO 账户名 [WITH GRANT OPTION]; |
表级权限 | GRANT 权限列表 ON 数据库名.表名 TO 账户名 [WITH GRANT OPTION]; |
列级权限 | GRANT 权限类型(字段列表)[, ...] ON 数据库名.表名 TO 账户名 [WITH GRANT OPTION]; |
存储过程权限 | GRANT EXECUTE | ALTER ROUTINE | CREATE ROUTINE ON {[*.* | 数据库名.*] | PROCEDURE 数据库名.存储过程} TO 账户名 [WITH GRANT OPTION]; |
代理权限 | GRANT PROXY ON 账户名 TO 账户名1 [, 账户名2 ] … [WITH GRANT OPTION]; |
需要注意的是,要想使用GRANT语句为用户授权,必须要拥有GRANT OPTION 权限;且在启用read_only 系统变量时,还必须要拥有 SUPER 权限。
下面以授予 test1 用户 shop.sh_goods 表的 SELECT 权限,以及对name和price字段的插入权限为例。
GRANT SELECT, INSERT (name, price) ON shop.sh_goods TO 'test1'@'%';
SELECT 权限是表级权限(shop.sh_goods表),INSERT是列级权限(shop.sh_goods表中的name和price字段)。
在SQL语句执行成功后,若要查看 test1 的权限,可以到 mysql.tables_priv 中查看表权限,到mysql.columns_priv 中查看列权限。
SELECT db, table_name, table_priv, column_priv FROM mysql.tables_priv WHERE user = 'test1';
+-----+-----------+-----------+------------+
|db |table_name |table_priv |column_priv |
+-----+-----------+-----------+------------+
|shop |sh_goods |Select |Insert |
+-----+-----------+-----------+------------+
SELECT db, table_name, column_priv, column_priv FROM mysql.tables_priv WHERE user = 'test1';
+-----+-----------+------------+------------+
|db |table_name |column_priv |column_priv |
+-----+-----------+------------+------------+
|shop |sh_goods |name |Insert |
|shop |sh_goods |price |Insert |
+-----+-----------+------------+------------+
在MySQL中,为了保证数据库的安全,需要将用户不必要的权限回收。
例如,数据管理员发现某个用户不应该具有 DELETE 权限,就应该及时将其收回。为此,MySQL专门提供了一个 REVOKE 语句用于回收指定用户的权限。
REVOKE 权限类型 [(字段列表)] [, 权限类型[(字段列表)]] ...
ON [目标类型] 权限级别 FROM 账户名[, 账户名] ...
权限类型、目标类型以及权限级别与授予权限 GRANT 的参数相同。
下面以删除test1用户对 shop.sh_goods 表的 name 和 price 字段的插入权限为例。
REVOKE INSERT (name, price) ON shop.sh_goods FROM 'test'@'%';
当用户的权限比较多,并且想要一次性将其全部收回时,使用上述语句就会比较麻烦,为此,可以使用以下的方式回收权限。
# 回收数据、结构、管理所有的权限以及可为其他用户授权的权限
REVOKE ALL [PRIVILEGES], GRANT OPTION FROM 账户名 [, 账户名] ...
#回收用户的代理权限
REVOKE PROXY ON 账户名 FROM 账户名1 [, 账户名2] ...
“ALL [PRIVILEGES]” 中的 PRIVILEGES 在使用时可以省略,它表示除去GRANT OPTION(授予权限)和 PROXY(代理权限)外的所有权限。
刷新权限指的是从系统数据库mysql中的权限表中重新加载用户的权限。原因在于GRANT、CREAT EUSER 等操作会将服务器的缓存信息保存到内存中,而 REVOKE、DROP USER 操作并不会同步到内存中,因此可能会造成服务器内存的消耗,所以在 REVOKE、DROP USER 后推荐读者使用MySQL 提供的 FLUSH PRIVILEGES 重新加载用户的权限。
FLUSH PRIVILEGES;
另外,刷新权限也可以利用msyqladmin命令完成。
# 方式1
msyqladmin -uroot -p reload
#方式2
msyqladmin -uroot -p flush-privileges
上述操作命令在执行时,首先会提示输入root用户的密码。其中,reload 与 flush-privileges 都可以用于重载权限信息。
视图是从一个或多个表中导出来的表,它是一种虚拟存在的表,表的结构和数据都依赖于基本表。
通过视图不仅可以看到存放在基本表中的数据,还可以像操作基本表一样,对数据进行查询、添加、修改和删除。
与直接操作基本表相比,视图具有如下优点。
简化查询语句。
安全性。
逻辑数据独立性。
CREATE [OR REPLACE] [ALGORITHM = (UNDEFINED | MERGE | TEMPTABLE)]
[DEEINER = { user | CURRENT_USER }]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
说明:
CREATE:表示创建视图的关键字。
OR REPLACE:可选,表示替换已有视图。
ALGORITHM:可选,表示视图算法,会影响查询语句的解析方式,它的取值有如下3个,一般情况下使用UNDEFINED即可。
UNDEFINED(默认):由MySQL自动选择算法。
MERGE:将select_statement和查询视图时的SELECT语句合并起来查询。
TEMPTABLE:先将select_statement的查询结果存入临时表,然后用临时表进行查询。
DEFINER:可选,表示定义视图的用户,与安全控制有关,默认为当前用户。
SQL SECURITY:可选,用于视图的安全控制,它的取值有如下两个。
DEFINER(默认):由定义者指定的用户的权限来执行。
INVOKER:由调用视图的用户的权限来执行。
view_name:表示要创建的视图名称。
column_list:可选,用于指定视图中的各个列的名称。默认情况下,与SELECT语句查询的列相同。
AS:表示视图要执行的操作。
select_statement:一个完整的查询语句,表示从某些表或视图中查出某些满足条件的记录,将这些记录导入视图中。
WITH CHECK OPTION:可选,用于视图数据操作时的检查条件。若省略此子句,则不进行检查。它的取值有如下两个。
CASCADED(默认):操作数据时要满足所有相关视图和表定义的条件。例如,当在一个视图的基础上创建另一个视图时,进行级联检查。
LOCAL:操作数据时满足该视图本身定义的条件即可。
提示:
在默认情况下,新创建的视图保存在当前选择的数据库中。若要明确指定在某个数据库中创建视图,在创建时应将名称指定为 数据库名.视图名
。
在 SHOW TABLES 的查询结果中会包含已经创建的视图。
创建视图要求用户具有CREATE VIEW 权限,以及查询涉及的列的 SELECT 权限。如果还有OR REPLACE 子句,必须具有视图的 DROP 权限。
在同一个数据库中,视图名称和已经存在的表名称不能相同,为了区分,建议在命名时添加 view_
前缀或 _view
后缀。
视图创建后,MySQL就会在数据库目录中创建一个 视图名.frm
文件。
在多表上创建视图
除了在单表上创建视图,还可以在两个或者两个以上的基本表上创建视图。下面通过案例演示在sh_goods 和 sh_goods_category 两张表上创建视图。
CREATE VIEW view_goods_cate AS
SELECT g.id, g.name, c.name category_name FROM sh_goods
LEFT JOIN sh_goods_category c
ON g.category_id = c.id;
当在创建视图时指定的 SELECT 语句涉及多张表的查询时,创建的视图就是多表视图。
自定义列名称
通过创建视图的语法格式可知,视图的列名称可以自定义。下面通过案例演示在创建视图时自定义列名称,具体SQL语句和运行结果如下。
CREATE VIEW view_goods_promo (sn, title, promotion_price) AS
SELECT id, name, price * 0.8 FROM sh_goods;
从上述结果可以看出,在创建视图时,自定义列名称的顺序与 AS 后 SELECT 字段列表的顺序一致,即 sn 对应 id,title 对应 name,promotion_price 对应 price * 0.8。
❗自定义列名称的数量必须与SELECT字段列表的数量一致,若不一致,MySQL会报错,视图将无法创建。
视图安全控制
从创建视图的语法格式可知,在创建视图时指定 DEFINER 和 SQL SECURITY 可以控制视图的安全。
下面通过案例演示它们的作用。
# 创建测试用户 shop_test
CREATE USER shop_test;
# 创建第1个视图,权限控制使用默认值
CREATE VIEW view_goods_t1 AS SELECT id, name FROM sh_goods LIMIT 1;
# 创建第2个视图,设置 DEFINER 为 shop_test 用户
CREATE DEFINER = 'shop_test' VIEW view_goods_t2 AS SELECT id, name FROM sh_goods LIMIT 1;
# 创建第3个视图,设置 SQL SECURITY 为 INVOKER
CREATE SQL SECURITY INVOKER VIEW view_goods_t3 AS SELECT id, name FROM sh_goods LIMIT 1;
# 为shop_test用户赋予前面创建的3个视图的SELECT权限
GRANT SELECT ON view_goods_t1 TO 'shop_test';
GRANT SELECT ON view_goods_t2 TO 'shop_test';
GRANT SELECT ON view_goods_t3 TO 'shop_test';
在上述操作创建的3个视图中,view_goods_t1 的 DEFINER 为当前用户root,SQL SECURITY为DEFINER;view_goods_t2 的 DEFINER 为 shop_test 用户,SQL SECURITY 同样也是DEFINER;view_goods_t3 的 DEFINER 为当前用户 root,但SQL SECURITY 为 INVOKER。
查看视图,是指查看数据库中已经存在的视图的定义。查看视图必须要有SHOW VIEW的权限。
查看视图的字段信息
MySQL提供的 DESCRIBE(DESC) 视图名
语句不仅可以查看数据表的字段信息,还可以查看视图的字段信息。
查看视图状态信息
MySQL提供的 SHOW TABLE STATUS '视图名 \G'
语句不仅可以查看数据表的状态信息,还可以查看视图的状态信息。
查看视图的创建语句
使用 SHOW CREATE VIEW
或 SHOW CREATE TABLE 视图名 \G
语句可以查看创建视图时的定义语句以及视图的字符编码。
修改视图是指修改数据库中存在的视图的定义。当基本表中的某些字段发生变化时,视图必须修改才能正常使用。在MySQL中修改视图的方式有两种,具体如下。
替换已有的视图
通过 CREATE OR REPLACE VIEW
语句可以在创建视图时替换已有的同名视图,如果视图不存在,则创建一个视图。
# 创建视图
CREATE VIEW view_goods AS SELECT id, name, price FROM sh_goods;
#修改以有视图
CREATE OR REPLACE VIEW view_goods AS SELECT id, name FROM sh_goods;
使用 ALTER VIEW 语句修改视图
ALTER [ALGORITHM = (UNDEFINED | MERGE | TEMPTABLE)]
[DEEINER = { user | CURRENT_USER }]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
ALTER 后面内容的各部分子句与 CREATE VIEW 语句中的子句含义相同。
DROP VIEW [IF EXISTS] 视图名1 [,视图名2]...
视图数据操作就是通过视图来查询、添加、修改、删除基本表中的数据。
视图是一个虚拟表,不保存数据,当通过视图来操作数据时,实际操作的是基本表中的数据
使用 INSERT 语句可以通过视图向基本表中添加数据 。
使用 UPADTE 语句可以通过视图修改基本表中的数据。
使用 DELETE 语句可以通过视图删除基本表中的数据。
这些语句使用方法与前面讲解添加数据的方式一样,只需将数据表名换成视图表名即可。
视图检查条件
在创建视图的语法格式中,WITH CHECK OPTION 子句用于在视图数据操作时进行条件检查。
# 创建第一个视图
CREATE VIEW view_cate_t1 AS SELECT id, name, FROM sh_goods_category WHERE id < 30;
# 创建第二个视图,使用CASCADED(级联)检查
CREATE VIEW view_cate_t2 AS SELECT id, name, FROM view_cate_t1 WHERE id > 20
WITH CHECK OPTION; #相当于 WITH CASCADED CHECK OPTION
# 插入数据,id必须大于20小于30才可以插入成功
# 创建第三个视图,使用LOCAL(本视图,非级联)检查
CREATE VIEW view_cate_t3 AS SELECT id, name, FROM view_cate_t1 WHERE id > 20
WITH LOCAL CHECK OPTION;
# 插入数据,只需满足id大于20即可插入
在MySQL中,事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,且每个SQL语句是相互依赖的。只要在程序执行过程中有一条SQL语句执行失败或发生错误,则其他语句都不会执行。也就是说,事务的执行要么成功,要么就返回到事务开始前的状态,这就保证了同一事务操作的同步性和数据的完整性。
MySQL中的事务必须满足A、C、I、D这4个基本特性,具体如下。
原子性
原子性(Atomicity)是指一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。
事务中如果有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。
一致性
一致性(Consistency)是指在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统不会返回到一个未处理的事务中。
MySQL中的一致性主要由日志机制实现,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。
隔离性
隔离性(Isolation)是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。
隔离性相关的技术有并发控制、可串行化、锁等。当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性
持久性(Durability)是指事务一旦提交,其对数据库的修改就是永久性的。需要注意的是,事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所有提交的数据可能都会丢失。
在默认情况下,用户执行的每一条SQL语句都会被当成单独的事务自动提交。如果要将一组SQL语句作为一个事务,则需要先执行以下语句显式地开启一个事务。
START TRANSACTION;
上述语句执行后,每一条SQL语句不再自动提交,用户需要使用以下语句手动提交,只有事务提交后,其中的操作才会生效。
COMMIT;
如果不想提交当前事务,可以使用如下语句取消事务(即回滚)。
ROLLBACK;
需要注意的是,ROLLBACK 只能针对未提交的事务回滚,已提交的事务无法回滚。当执行COMMIT或ROLLBACK后,当前事务就会自动结束。
MySQL中的事务不允许嵌套,若在执行 START TRANSACTION 语句前上一个事务还未提交,会隐式地执行提交操作。
事务处理主要是针对数据表中数据的处理,不包括创建或删除数据库、数据表,修改表结构等操作,而且执行这类操作时会隐式地提交事务。
MySQL默认是自动提交模式,如果没有显式开启事务(START TRANSACTION),每一条SQL语句都会自动提交(COMMIT)。如果用户想要控制事务的自动提交方式,可以通过更改AUTOCOMMIT变量来实现,将其值设为1表示开启自动提交,设为0表示关闭自动提交。若要查看当前会话的AUTOCOMMIT值,使用如下语句。
SELECT @@autocommit;
若要关闭当前会话的事务自动提交,可以使用如下语句。
SET AUTOCOMMIT =0;
在回滚事务时,事务内所有的操作都将撤销。而若希望只撤销一部分,可以用保存点来实现。使用以下语句可以在事务中设置一个保存点。
SAVEPOINT 保存点名;
在设置保存点后,使用以下语句可以将事务回滚到指定保存点。
ROLLBACK TO SAVEPOINT 保存点名;
使用时若不再需要一个保存点,可以使用如下语句删除保存点。
RELEASE SAVEPOINT 保存点名;
一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除。另外,在回滚到某个保存点后,在该保存点之后创建过的保存点也会消失。
事务的提交(COMMIT)和回滚(ROLLBACK)还有一些可选子句,如下所示。
COMMIT [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [AND [NO] CHAIN] [[NO] RELEASE]
AND CHAIN 用于在当前事务结束时,立即启动一个新事务,并且新事务与刚结束的事务有相同的隔离级;
RELEASE用于在终止当前事务后,让服务器断开与客户端的连接。若添加NO,则表示抑制 CHAIN 和 RELEASE 完成。
由于数据库是一个多用户的共享资源,MySQL允许多线程并发访问,因此用户可以通过不同的线程执行不同的事务。为了保证这些事务之间不受影响,对事务设置隔离级是十分必要的。
查看隔离级别
# 查看全局隔离级
SELECT @global.transaction_isolation;
# 查看当前会话中的隔离级
SELECT @@session.transaction_isolation;
# 查看下一个事务的隔离级
SELECT @@transaction_isolation;
在以上语句中,全局的隔离级影响的是所有连接MySQL的用户,而当前会话的隔离级别只影响当前正在登录MySQL服务器的用户,不会影响其他用户。而下一个事务的隔离级别仅对当前用户的下一个事务操作有影响。
在默认情况下,上述3种方式返回的结果都是REPEATABLE-READ,表示隔离级别为可重复读。
修改隔离级别
在MySQL中,事务的隔离级别可以通过SET语句进行设置,具体语法如下。
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL 参数值
SET 后的 SESSION 表示当前会话,GLOBAL表示全局,若省略表示设置下一个事务的隔离级。
TRANSACTION 表示事务,ISOLATION 表示隔离,LEVEL 表示级别。参数值可以是 READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
或 SERIALIZABLE
中的一种。
只读事务
默认情况下,事务的访问模式为 READ WRITE(读/写模式),表示事务可以执行读(查询) 或写(更改、插入、删除等)操作。若开发需要,可以将事务的访问模式设置为 READ ONLY(只读模式),禁止对表进行更改。
# 设置只读事务
SET [SESSION | GLOBAL] TRANSACTION READ ONLY
# 恢复成读写事务
SET [SESSION | GLOBAL] TRANSACTION READ WRITE
读取未提交
READ UNCOMMITTED 是事务中最低的级别,在该级别下的事务可以读取到其他事务中未提交的数据,这种读取的方式也被称为脏读(Dirty Read)。简而言之,脏读是指一个事务读取了另外一个事务未提交的数据。
例如,Alex 要给 Bill 转账100元购买商品,Alex开启事务后转账,但不提交事务,通知Bill来查询,如果Bill的隔离级别较低,就会读取到Alex的事务中未提交的数据,发现Alex确实给自己转了100元,就给Alex发货。等Bill发货成功后,Alex将事务回滚,Bill就会受到损失,这就是脏读造成的。
读取提交
READ COMMITTED 是大多数 DBMS (如 SQL Server、Oracle) 的默认隔离级,但不包括MySQL。
在该隔离级下只能读取其他事务已经提交的数据,避免了脏读数据的现象。但是在该隔离级别下,会出现不可重复读(NON-REPEATABLE READ)的问题。
不可重复读是指在一个事务中多次查询的结果不一致,原因是查询的过程中数据发生了改变。
例如,在网站后台统计所有用户的总金额,第1次查询Alex有900元,为了验证查询结果,第2次查询Alex有800元,两次查询结果不同,原因是第2次查询前Alex取出了100元。
可重复读
REPEATABLE READ 是MySQL的默认事务隔离级,它解决了脏读和不可重复读的问题,确保了同一事务的多个实例在并发读取数据时,会看到同样的结果。但在理论上,该隔离级会出现幻读(PHANTOM READ)的现象。
幻读又被称为虚读,是指在一个事务内两次查询中数据条数不一致,幻读和不可重复读有些类似,同样发生在两次查询过程中。不同的是,幻读是由于其他事务做了插入记录的操作,导致记录数有所增加。不过,MySQL的InnoDB存储引擎通过多版本并发控制机制解决了幻读的问题。
例如,在网站后台统计所有用户的总金额时,当前只有两个用户,总金额为2000元,若在此时新增一个用户,并且存入1000元,再次统计时发现总金额变为3000元,造成了幻读的情况。
可串行化
SERIALIZABLE 是最高级别的隔离级,它在每个读的数据行上加锁,使之不会发生冲突,从而解决了脏读、不可重复读和幻读的问题。但是由于加锁可能导致超时(Timeout) 和 锁竞争(Lock Contention)现象,因此 SERIALIZABLE 也是性能最低的一种隔离级。除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。
数据库编程,引擎,索引,锁,分区等内容有时间在更新。。。