MySQL 相关笔记
MySQL
基础知识
1 数据库相关概念
名称 | 简称 | 作用 |
---|---|---|
数据库 | DataBase (DB) | 存储数据的仓库,数据是有组织的进行存储 |
数据库操作系统 | DataBase Management System (DBMS) | 操纵和管理数据库的大型软件 |
SQL | Structured Query Language | 操作关系型数据库的编程语言,定义了一套操作关系型数据库的统一语言 |
常见的 DBMS:Oracle
、MySQL
、SQL Server
等。我们以 MySQL
作为课程的讲解的管理系统。
2 MySQL
数据库的数据模型
关系型数据库(RDBMS):建立在关系模型基础上,由多张相关联的二维表 Table 组成的数据库。如
MySQL
数据库的结构为:
3 SQL 语句
3.1 SQL
的通用语法
SQL通用语法
-
SQL
语句可以单行或多行书写,以分号结尾; -
SQL
语句可以使用空格或缩进来增强语句的可读性; -
MySQL
数据库的SQL
语句不区分大小写,关键字建议使用大写; -
注释:
-
单行注释:
1
2
3-- 注释内容
或
#注释内容(MySQL特有) -
多行注释:
1
/* 注释内容*/
3.2 SQL
的分类
分类 | 全称 | 说明 |
---|---|---|
DDL |
Data Definition Language | 数据定义语言,用来定义数据库对象(数据库、表、字段名(列名)、索引等) |
DML |
Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增、删、改操作 |
DQL |
Data Query Language | 数据查询语言,用来对数据库表中的记录进行查询操作 |
DCL |
Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限等 |
3.3 DDL
数据定义语句
数据库相关操作
SHOW
查询所有数据库
1 | SHOW DATABASES; |
CREATE
创建数据库
1 | CREATE DATABASE [IF NOT EXISTS] '数据库名' [DEFAULT CHARSET 字符集] [COLLATE 排序规则]; |
例如
1 | CREATE TABLE IF NOT EXISTS 'test' DEFAULT CHARSET utf8 COLLATE DEC; |
USE
切换数据库
1 | USE '数据库名'; |
SELECT DATABASE()
显示当前数据库
1 | SELECT DATABASE(); |
DROP
删除数据库
1 | DROP DATABASE [IF EXISTS] '数据库名'; |
表相关操作
SHOW TABLES
查询当前数据库的所有表
1 | SHOW TABLES; |
DESC
查询表结构
1 | DESC 表名; |
查询指定表的建表语句
1 | SHOW CREATE TABLE 表名; |
CREATE
创建表
关系名(表名)、列名(属性名或字段名)、属性的数据类型、完整性约束
1 | CREATE TABLE '表名' ( |
例如
1 | CREATE TABLE IF NOT EXISTS emp( |
ALTER
表中添加、修改属性
1 | ALTER TABLE '表名' ( |
DROP
删除表
1 | DROP TABLE [IF EXISTS] '表名'; |
TRUNCATE
格式化表
1 | -- 删除指定表,并重新创建该表 |
数据类型
(1)数值类型
类型 | 内存大小 | 范围(SIGNED ) |
范围(UNSIGNED ) |
类似 Java 中的类型 |
---|---|---|---|---|
TINYINT |
1 Byte | (-2^7, 2^7) | (0, 2^8-1) | btye |
SMALLINT |
2 Bytes | (-2^15, 2^15) | (0, 2^15-1) | short |
MEDIUMINT |
3 Bytes | (-2^23, 2^23) | (0, 2^24-1) | - |
INT 或 INTEGER |
4 Bytes | (-2^31, 2^31) | (0, 2^32-1) | int |
BIGINT |
8 Bytes | (-2^63, 2^64) | (0, 2^64-1) | long |
FLOAT |
4 Bytes | float |
||
DOUBLE |
8 Bytes | double |
||
DECIMAL |
- | 取决于精度与标度 | 取决于精度与标度 | - |
(2)字符串类型
类型 | 大小 | 描述 |
---|---|---|
CHAR |
0~2^8-1 Bytes | 定长字符串。例 CHAR(10) :10 Bytes 的固定长度,会补位。浪费空间。 |
VARCHAR |
0~2^16-1 Bytes | 变长字符串。例 VARCHAR(10) :最大 10 Bytes 的长度,与实际大小相关,不会补位。浪费性能。 |
TINYBLOB |
0~2^8-1 Bytes | 不超过 255 个字符串的二进制数据 |
TINYTEXT |
0~2^8-1 Bytes | 短文本字符串 |
BLOB |
0~2^16-1 Bytes | 二进制形式的长文本数据 |
TEXT |
0~2^16-1 Bytes | 长文本数据 |
MEDIUMBLOB |
0~2^24-1 Bytes | |
MEDIUMTEXT |
0~2^24-1 Bytes | |
LONGBLOB |
0~2^64-1 Bytes | |
LONGTEXT |
0~2^64-1 Bytes |
(3)日期时间类型
类型 | 大小 | 格式 | 格式 | 描述 |
---|---|---|---|---|
DATE |
3 Bytes | 1000-01-01 至 9999-12-31 | YYYY-MM-DD |
日期值 |
TIME |
3 Bytes | -838:59:59 至 838:59:59 | HH:MM:SS |
时间值或持续时间 |
YEAR |
1 Bytes | 1901 至 2155 | YYYY |
年份值 |
DATETIME |
8 Bytes | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS |
混合日期和时间 |
TIMESTAMP |
4 Bytes | 1970-01-01 00:00:01 至 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS |
时间戳:混合日期和时间 |
3.4 DML
数据操作语言
INSERT
插入语句
1 | -- 将指定元组加入指定表中 |
例如
1 | INSERT INTO table_test(id, name, age, gender) VALUES 2, 'Mike', 20, '男'; |
UPDATE
修改语句
1 | UPDATE 表名 |
例如
1 | -- 修改 table_test 表中 ID为2 的name属性为Neil |
DELETE
删除语句
1 | UPDATE |
例如
1 | DELETE FROM table_test WHERE id = 2; |
3.5 DQL
数据查询语言(单表)
DQL(Data Query Language)
数据查询语言,用来查询数据库记录的语言。
DQL
语法:
1 | SELECT 字段列表 |
常见的聚合函数
常用聚合函数:null
值不参与统计
-
COUNT (列名)
:统计记录的个数(行数)1
SELECT COUNT(id) FROM table_test;
-
SUM (列名)
:计算列总和 -
AVG (列名)
:计算列平均值 -
MAX (列名)
:计算列最大值 -
MIN (列名)
:计算列最小值
SELECT
查询语句
1 | -- 当满足_condition的条件下,从表T中查询返回去重的列A |
- 查询多个字段
1 | SELECT 字段1, 字段2,... FROM 表名; |
- 设置别名
1 | SELECT 字段1[as 别名1], 字段2[as 别名2],... FROM 表名; |
- 返回去重的查询结果
1 | SELECT DISTINCT * FROM 表名; |
WHERE
条件查询
WHERE
:条件子句,可以用 AND / OR
连接多个条件
-
【比较大小】:
=, >, <, >=, <=, <>, !<, !>, NOT
1
SELECT * FROM table_test WHERE id > 1
-
【确定范围 (包含)】:
列名 [NOT] BETWEEN ... AND...
1
SELECT DISTINCT * FROM table_test WHERE age BETWEEN 10 AND 40;
-
【指定集合】:
列名 [NOT] IN (Val1, Val2, Val3)
,意为在查询结果中筛选列名的值为Val1, Val2, Val3
的记录1
SELECT * FROM table_test WHERE age IN (13, 15, 16);
-
【字符串匹配】:
列名 [NOT] LIKE '匹配目标串' [ESCAPE '转义字符']
-
%
通配符:代表任意长度的字符串。a%b
表示以a
开始,b
结束的任意长度的字符串1
SELECT * FROM table_test WHERE name LIKE 'M%e';
-
_
通配符:代表单个字符。a_b
表示a开始,b结束的长度为3的字符串1
SELECT * FROM table_test WHERE name like 'Ni_l';
-
-
【涉及空值】:
IS [NOT] NULL
1
SELECT * FROM table_test WHERE name IS NULL;
GROUP BY
分组查询
【GROUP BY
】:对查询结果分组,
分组方法:按指定的一列或多列值分组,值相等的为一组。
HAVING
:功能类似于WHERE
(分组前的数据),但是作用域是分组后的数据(HAVING
)
1 | -- 1. 根据性别分组,统计男性与女性的数量 |
ORDER BY
排序查询
【ORDER BY
】:对查询结果进行排序
1 | SELECT * FROM table_test ORDER BY 列名1 DESC, 列名2 DESC, ...; |
列名1,列名2 ASC
:升序(默认,可省略)列名1,列名2 DESC
:降序
1 | -- 1. 根据年龄对所有人进行升序/降序排序 |
LIMIT
分页查询
【关键字】:LIMIT
1 | SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数; |
注意 ⚠️:
- 【起始索引】从 0 开始,
LIMIT
的功能,在不同数据库管理系统中有不同的实现,mySQL
中是LIMIT
- 如果查询的是第一页的内容,起始索引可以省略,直接简写为
LIMIT 查询记录数
1 | -- 1. 查询第1页的数据,每页展示10条记录 |
DQL
的执行顺序
1 | SELECT 字段列表 -- 4 |
DQL
的执行顺序:(可用 AS 别名
验证)
(1)FROM 表名
(2)WHERE 条件
(3)GROUP BY 分组 [HAVING 分组后的条件]
(4)SELECT 字段
(5)ORDER BY 排序
(6)LIMIT 分页
3.6 DCL
数据控制语言
DCL (Data Control Language)
数据控制语言,用来管理数据库用户,及其访问数据库的权限。
管理用户
-
【查询用户】:在
mySQL
数据库中,用户的信息都存放在user
表中1
2USE mysql;
SELECT * FROM user; -
【创建用户】:
1
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
- 当主机名为
%
时,表示任意主机的通配符
例如:
1
2
3
4
5-- 1. 创建一个用户gdai,他只能够在当前主机localhost访问,密码123456
CREATE USER 'gdai'@'localhost' IDENTIFIED BY '123456';
-- 2. 创建一个用户gdai,他可以在任意主机访问该数据库,密码123456
CREATE USER 'gdai'@'%' IDENTIFIED BY '123456'; - 当主机名为
-
【修改用户密码】
1
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '密码';
例如:
1
2-- 修改用户gdai的密码为1111
ALTER USER 'gdai'@'%' IDENTIFIED WITH mysql_native_password BY '1111'; -
【删除用户】
1
DROP USER '用户名'@'主机名';
例如:
1
2-- 删除用户gdai
DROP USER 'gdai'@'%';
管理权限
mySQL
数据库中定义了许多种权限,但是常用的有以下几种:
权限 | 说明 |
---|---|
ALL 或 ALL PRIVILEGES |
所有权限 |
SELECT |
查询数据的权限 |
INSERT |
插入数据的权限 |
UPDATE |
修改数据的权限 |
DELETE |
删除数据的权限 |
ALTER |
修改表/字段的权限 |
DROP |
删除数据库/表/视图的权限 |
CREATE |
创建数据库/表的权限 |
-
查询权限
1
SHOW GRANTS FOR '用户名'@'主机名';
-
授予权限
1
GRANT 权限名 ON 数据库名.表名 TO '用户名'@'主机名';
-
撤销权限
1
REVOKE 权限名 ON 数据库名.表名 FROM '用户名'@'主机名';
例如:
1 | -- 查询用户gdai的权限 |
4 函数
我们在之前 DQL
中提到过 COUNT
、AVG
、SUM
等聚合函数,本小节中,我们将详细介绍 mySQL
中内置函数相关的内容。
【函数】是指一段可以被另一段程序直接调用的程序或代码。
4.1 字符串函数
mySQL
数据库中常用的字符串函数有以下几种:
函数名 | 功能 |
---|---|
CONCAT(s1, s2, s3, ..., sn) |
字符串拼接,将 s1,...,sn 拼接成一个字符串 |
LOWER(str) |
将 str 全部转为小写 |
UPPER(str) |
将 str 全部转为大写 |
LPAD(str, n, pad) |
左填充。用字符串 pad 对 str 的左边进行填充,直至达到 n 个字符长度 |
RPAD(str, n, pad) |
右填充。用字符串 pad 对 str 的右边进行填充,直至达到 n 个字符长度 |
TRIM(str) |
去除字符串头部与尾部的空格 |
SUBSTRING(str, start, len) |
截取子串。在字符串 str 的 strat 处截取长度为 len 的子串 |
4.2 数值函数
mySQL
数据库中常用的数值函数有以下几种:
函数 | 功能 |
---|---|
CIEL(x) |
x 向上取整 |
FLOOR(x) |
x 向下取整 |
MOD(x,y) |
返回 x mod y |
RAND |
0-1 范围内的随机数 |
ROUND(x,y) |
对 x 四舍五入,保留小数点后 y 位 |
4.3 日期函数
mySQL
数据库中常用的日期函数有以下几种:
函数 | 功能 |
---|---|
CURDATE() |
返回当前日期 |
CURTIME() |
返回当前时间 |
NOW() |
返回当前日期与时间 |
YEAR(date) |
返回指定 date 的年份 |
MONTH(date) |
返回指定 date 的月份 |
DAY(date) |
返回指定 date 的天 |
DATE_ADD(date, INTERVAL expr type) |
返回指定 date 加上一个时间间隔 expr 后的值 |
DATEDIFF(date1, date2) |
返回 date1 与 dete2 之间的天数 |
4.4 流程控制函数
mySQL
数据库中常用的流程函数有以下几种:
函数 | 功能 |
---|---|
IF(value, t, f) |
如果 value 为真,返回 t ,否则返回 f |
IFNULL(value1, value2) |
如果 value1 不为空,返回 value1 ,否则返回 value2 |
CASE WHEN val1 THEN res1 WHEN val2 THEN res2 ... ELSE default END |
如果 val1 为真,返回 res1 ;如果 val2 为真,返回 res2 ,…,否则返回 default |
CASE expr WHEN val1 THEN res1 WHEN val2 THEN res2 ... ELSE default END |
如果 expr 的值为 val1 ,返回 res1 ;如果为 val2 ,返回 res2 ,…,否则返回 default |
5 约束
- 【概念】:约束是作用于表中字段上的规则,用来限制存储在表中的数据。
- 【目的】:用来保证数据库中数据的正确性、有效性和完整性。
常见的约束有以下几种:
约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制该字段的数据非空 | NOT NULL |
唯一约束 | 保证该字段的所有数据都是唯一的 | UNIQUE |
主键约束 | 主键是一条记录的唯一标识,要求非空且唯一 | PRIMARY KEY |
外键约束 | 用来在多张表的数据之间建立连接,保证数据的一致性和完整性 | FOREIGN KEY |
检查约束(ver 8.0.16 之后) |
保证字段值满足某一个条件 | CHECK(expr) |
默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 | DEFAULT |
自增约束(主键) | 保证主键字段的数据自增 | AUTO_INCREMENT |
5.1 外键约束
【概念】:在多张表的数据之间建立连接,保证数据的一致性和完整性。
我们将拥有外键的表称为【从表】,外键所关联的表称为【主表】。
添加外键约束
1 | ALTER TABLE 表名 ADD CONSTRAINT 外键名称 |
例如,我们有如下两个表,Client
和 Account
。我们想在 Account
表中添加一个名为 client_ID
的外键。
1 | ALTER TABLE Account ADD CONSTRAINT fk_client_ID_in_Account |
删除外键约束
1 | ALTER TABLE 表名 DROP FOREIGN KEY 外键名称; |
例如:
1 | ALTER TABLE Account DROP FOREIGN KEY fk_client_ID_in_Account; |
更新/删除策略
mySQL
数据库提供以下几种更新策略:
行为 | 说明 |
---|---|
NO ACTION |
默认。当在主表中删除/更新相应记录时,首先检查该记录是否被关联作外键,如果有则不允许删除/更新(同 RESTRICT )。 |
RESTRICT |
默认。当在主表中删除/更新相应记录时,首先检查该记录是否被关联作外键,如果有则不允许删除/更新(同 NO ACTION )。 |
CASCADE |
当在主表中删除/更新相应记录时,首先检查该记录是否被关联作外键,如果有则同时删除/更新从表中的记录. |
SET NULL |
当在主表中删除相应记录时,首先检查该记录是否被关联作外键,如果有则在从表中将相关记录的字段值设置为 null |
SET DEFAULT |
当在主表中设置相应记录时,首先检查该记录是否被关联作外键,如果有则在从表中将相关记录的字段值设置为 default 值。(innoDB 不支持) |
6 多表查询
在之前的 DQL
的学习中,我们涉及的都是单表查询。这一小节我们介绍多表查询。
6.1 多表关系
在数据库结构的设计中,常常会根据业务需求以及业务模块间的关系,分析并设计表结构。由于业务之间相互关联,所以各个表之间也存在着各种联系,基本上分为三种:
- 一对多(多对一)
- 多对多
- 一对一
一对多(多对一)关系
案例:客户 Client
与账户 Account
的对应关系。
关系:一个客户 Client
可以有多个账户 Account
,一个账户 Account
只能对应一个客户 Client
。
实现:在“多”的一端(从表)添加一个外键 指向“一”的一端(主表)
1 | ALTER TABLE Account ADD CONSTRAINT fk_client_ID_in_Account |
多对多关系
案例:商品 Item
与品类 Category
的对应关系。
关系:一件商品 Item
可以对应很多中品类 Category
,一个品类 Category
中也可以包含多种商品 Item
。
实现:建立一张中间表 t_Item_Category
,中间表中至少包含两个外键,分别关联双方的主键。
1 | -- 创建中间表 t_Item_Category |
一对一关系
案例:经理 Manager
与部门 Department
之间的关系
关系:一个经理 Manager
只能任职一个部门 Department
;一个部门 Department
只能有一个经理 Manager
。
实现:在任意一方加入外键(从表),关联另一方(主表)的主键,并且设置外键为 UNIQUE
1 | ALTER TABLE Department ADD CONSTRAINT fk_ManagerID |
6.2 多表查询概述
连接查询
【连接查询】又分为:内连接和外连接。
【内连接】
又叫等值连接。相当于查询A、B两张表交集的部分。
-
等值连接
1
2-- 等值连接
SELECT * FROM 表1,表2 WHERE 表1字段=表2字段; -
内连接有两种查询语法:
-
隐式内连接
1
SELECT 字段列表 FROM 表1,表2 WHERE 连接条件;
-
显式内连接:
1
SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 连接条件;
-
举例:
1 | -- 1. 隐式内连接:查询每个员工的姓名(emp表),及关联的部门的名称(dept表) |
查询结果如下:
注意 ⚠️:
在上述内查询中,我们可以注意到
emp.id = 2
的员工对应的dept_id = null
,在内连接中无法查询到到该条记录。
【外连接】
-
左外连接:查询左表 A的所有数据,以及两张表交集的部分
1
SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 连接条件;
-
右外连接:查询右表 B的所有数据,以及两张表交集的部分
1
SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 连接条件;
举例:
【左外连接】查询:
1 | -- 1. 左外连接:查询每个员工的姓名(emp表),及关联的部门的名称(dept表) |
查询结果如下:
【右外连接】查询:
1 | -- 2. 右外连接:查询每个员工的姓名(emp表),及关联的部门的名称(dept表) |
查询结果如下:
【自连接】
顾名思义,就是自己与自己的连接查询。查询时需要给表起别名。
1 | SELECT 字段列表 FROM 表1 AS 表1别名 [LEFT] JOIN 表1 AS 表1别名 ON 连接条件; |
自连接查询可以是内连接查询,也可以是外连接查询。
举例:
1 | -- 自连接:查询每个员工的姓名(emp表),及其管理人员的姓名(emp表) |
查询结果如下:
交叉连接(广义笛卡尔积):不常用
1 | SELECT col1.*,col2.* |
联合查询
对于 union
联合查询,就是把多个查询结果通过 UNION(去重)
和 UNION ALL(不去重)
合并起来,成为一个新的查询结果集。
1 | SELECT 字段列表 FROM 表A |
举例:
1 | -- 1. 查询薪资低于 8000 的员工,和年龄大于 24 岁的员工 |
查询结果为
注意 ⚠️
- 在联合查询中,查询的字段列表要保持一致,否则无法查询;
union all
会将所有查询结果合并在一起,union
会对合并之后的结果去重。
嵌套查询
在 **SQL
语句中嵌套 SELECT
语句,称为嵌套查询,又称子查询
1 | SELECT 列名 |
- 内层查询的结果是一个关于“内层列名”的集合
- 不能使用
ORDER BY
和GROUP BY
语句
根据嵌套查询返回结果的不同,可分为:
- 标量嵌套查询(返回的查询结果是单个值);
- 列嵌套查询(返回的查询结果是一列);
- 行嵌套查询(返回的查询结果是一行);
- 表嵌套查询(返回的查询结果是多行多列的表);
【标量嵌套查询】
标量嵌套查询返回的内层查询结果是单个值(数字、字符串、日期等)。
常用到的操作符:= <> > >= < <=
举例:
1 | -- 1 查询"研发部"的所有员工信息: |
查询结果为
【列嵌套查询】
列嵌套查询返回的内层查询结果是一列(即多行数据),再将一列数据作为输入参与外层查询。
常见的操作符有:
操作符 | 描述 |
---|---|
IN |
在指定的集合范围之内,多选一 |
NOT IN |
不在指定的集合范围之内 |
ANY |
在内层查询返回的结果中,有任意一个满足就可以 |
SOME |
同 ANY |
ALL |
内层查询返回的所有结果全部满足才可以 |
举例:
1 | -- 1 查询 "人力资源部" 和 "研发部" 的所有员工信息 |
查询结果:
【行嵌套查询】
行嵌套查询返回的内层查询结果是一行(即多列数据),再将一行数据作为输入参与外层查询。
常用到的操作符:= <> IN NOT IN
举例:
1 | -- 1 查询与Bob的薪资和部门领导相同的与员工信息 |
查询结果:
【表嵌套查询】
行嵌套查询返回的内层查询结果是多行多列数据(即表),再将这些数据作为输入参与外层查询。
常用到的操作符:IN
举例:
1 | -- 1 查询与Theo,Tom的年龄和部门相同的员工信息 |
查询结果:
例 2:
1 | -- 2 查询年龄大于24岁的员工信息,及其工作地点 |
查询结果:
6.3 练习
我们在数据库空定义如下图所示的表:
1 | -- 1. 准备数据 |
emp 表
:
dept 表
:
salaryGrade 表
:
需求 1
查询员工的姓名、年龄、职位、部门名称(使用隐式内连接)
1 | -- 1 查询员工的姓名、年龄、职位、部门名称(使用隐式内连接) |
查询结果:
需求 2
查询年龄小于30岁的员工的姓名、年龄、职位和部门名称(显式内连接)
1 | -- 2 查询年龄小于30岁的员工的姓名、年龄、职位和部门名称(显式内连接) |
查询结果:
需求 3
查询拥有员工的部门 ID 与部门名称
1 | -- 3 查询拥有员工的部门 ID 与部门名称 |
查询结果:
需求 4
查询所有年龄大于 40 岁的员工,及其所属的部门名称;如果有员工没有分配部门,也要展示出来。
1 | -- 4 查询所有年龄大于 40 岁的员工,及其所属的部门名称;如果有员工没有分配部门,也要展示出来。 |
查询结果:
需求 5
查询所有员工的工资等级
1 | -- 5 查询所有员工的工资等级 |
查询结果:
需求 6
查询“研发部”的所有员工的信息以及薪资等级
1 | -- 6 查询“研发部”的所有员工的信息以及薪资等级 |
查询结果:
需求 7
查询“研发部”的平均工资
1 | -- 7 查询“研发部”的平均工资 |
查询结果:
需求 8
查询薪资比“常遇春”高的员工信息。
1 | -- 8 查询薪资比“常遇春”高的员工信息。 |
查询结果:
需求 9
查询比平均工资高的员工信息
1 | -- 9 查询比平均工资高的员工信息 |
查询结果:
需求 10 *
查询低于本部门平均工资的员工信息。
1 | -- 10 查询低于本部门平均工资的员工信息。 |
查询结果:
需求 11
查询所有的部门信息,并统计部门的员工数量。
1 | -- 11 查询所有的部门信息,并统计部门的员工数量。(思路同上一题) |
查询结果:
7 事务
本小节我们会研究一种数据库操作中关于事务
的相关概念,包含
本章节单独作为一篇:事务 Transaction,点击跳转。
MySQL
进阶知识
在进阶篇章中,我们将分为以下几个部分:
- 存储引擎
- 索引
SQL
优化- 视图/存储过程/触发器
- 锁
innoDB
引擎mySQL
管理
1 存储引擎
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表 的,而不是基于库的,所以存储引擎也可以被称为表类型。
- 我们在创建表时,可以指定存储引擎(默认为
innoDB
):
1 | CREATE TABLE 表名( |
- 查看当前数据库支持的存储引擎:
1 | SHOW engines; |
其中,我们主要讲解 innoDB
、myISAM
和 memory
这三个引擎。
1.1 innoDB
存储引擎
innoDB
是一种兼顾高可靠性和高性能的通用存储引擎,在 mySQL v5.5
版本之后,innoDB
是默认的存储引擎。
特点
DML
操作(增删改)遵循ACID
模型,支持事务;- 支持行级锁,提高并发访问性能;
- 支持外键约束,保证数据的完整性和正确性
- 每个表有对应一个
.idb
的表空间文件,用来存储该表的结构、数据和索引。
逻辑存储结构
TableSpace
:表空间;Segment
:段;Extent
:区,大小为1M
,一个区包含 64 个页;Page
:页,大小为16K
;Row
:行,对应一条记录。
1.2 myISAM
存储引擎
myISAM
是 mySQL
早期默认的存储引擎。
特点
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
- 有 3 个文件存储一个表:
.sdi
文件存放表的结构信息;.myd
文件存放数据;.myi
文件存放索引
1.3 memory
存储引擎
memory
存储引擎的表数据存储在内存中,只能将这种表作为临时表或缓存使用。
特点
- 存放在内存
hash
索引.sdi
文件存放表结构
1.4 三种引擎的区别
innoDB |
myISAM |
memory |
|
---|---|---|---|
存储限制 | 64 TB |
有 | 有 |
事务安全 | 支持 | - | - |
锁机制 | 行锁 | 表锁 | 表锁 |
B+ Tree 索引 |
支持 | 支持 | 支持 |
Hash 索引 |
- | - | 支持 |
全文索引 | 支持 (v5.6 之后) |
支持 | - |
空间使用 | 高 | 低 | - |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
是否支持外键 | 支持 | - | - |
适用场景 | 对事务的完整性有高要求、 高并发条件、 除了增查操作外还有大量的改删操作 |
增查操作为主、 不要求高并发性 ( MangoDB ) |
常用于临时表和缓存、 无法保证数据的安全性 ( Redis ) |
2 索引
索引是帮助 mySQL
高效获取数据的(有序)数据结构。除了数据,DBMS
还需要维护这种指向数据的、满足特定查找算法的数据结构,这样就可以在索引上高效地查询数据。
假设我们将这种数据结构用二叉树实现,使其在 age
字段建立索引,指向数据的地址:(注意 ⚠️,此处只是用一个二叉树举例,并不是真实的索引结构)
1 | select * from emp where age=45; |
【索引的优缺点】
优点 | 缺点 |
---|---|
提高数据检索的效率,降低数据库的 IO 成本 | 索引需要额外的存储空间 |
通过索引对数据进行排序,可以降低数据排序的成本,降低 CPU 的消耗 | 索引大大提高了查询效率,但是却降低了更新表(update 、insert 和 delete )的速度 |
2.1 索引的结构
mySQL
的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
索引结构 | 描述 |
---|---|
B+ Tree 索引 |
最常见的索引结构,大部分引擎都支持 |
Hash 索引 |
底层通过哈希表实现,不支持范围查询,只支持精确查询 |
R-tree (空间索引) |
myISAM 的一个特殊索引结构,主要用于地理空间数据类型 |
Full-text (全文索引) |
是一种通过建立倒排索引,快速匹配文档的方法 |
2.2 B+ Tree
索引结构
在之前的例子中我们,我们使用了二叉树来充当索引,但是使用二叉树会存在两个问题:
- 如果我们按照一定顺序插入一组数据,该二叉树会变成一个链表,从而失去了索引的意义;
- 二叉树的一个节点只能有两个子节点,如果数据量较大,树的层级就会很深,检索依然很慢。
例如我们插入
36,22,48,19,33,45,53,17,20,23,34,40,46,50,70
时,会形成如下的二叉树如果我们插入
36,34,33,23,22,20,19,17
时,会形成如下的二叉树
我们知道,红黑树是一种自平衡二叉树,可以解决第一个问题,如下图所示。但是依旧不能解决数据量较大的问题。
如果我们不使用二叉树,而采用平衡的 n
叉树来建立索引,就可以解决上述的问题。这个平衡的 n
叉树就称为 B-Tree
(多路平衡查找树)。
B-Tree
(多路平衡查找树)
以一颗最大度树 (Max-Degree) 为 5 的五阶 B-Tree
为例(每个节点最多存储 4 个 key
,5 个指针):
我们可以在 B-Tree
Visualization 网站中插入如下数据,可视化模拟在 B-Tree
的插入操作。
1 | B-Tree: |
插入数据后的 B-Tree
如下所示:
这里提供一个 B-Tree
的 Java
实现,代码来自 algs4.cs.princeton.edu
:
1 | public class BTree<Key extends Comparable<Key>, Value> { |
1 |
|
经典 B+ Tree
结构
前面我们了解了 B-Tree
的索引结构。我们发现,在 B-Tree
中数据和索引是糅合在一起的,即数据部分既在叶子节点又在根节点。所以我们在 B-Tree
的基础上改良得到 B+ Tree
。即将所有的元素及其数据都存放在叶子结点上,根节点元素只用作索引,然后将所有的叶子节点使用链表连接。如下图:
同样的,我们可以在 B+ Tree
Visualization 网站中插入如下数据,可视化模拟在 B+ Tree
的插入操作。
1 | B-Tree: |
mySQL
的 B+ Tree
索引结构
mySQL
索引结果对经典的 B+ Tree
进行了优化:在原来的基础上,增加了一个执行相邻叶子结点的指针,就形成了带有顺序的 B+ Tree
,以提高区间访问及排序能力。如下图
2.3 Hash
索引
Hash
索引就是采用一定的Hash
算法,将需要索引的字段值换算成新的Hash
值,映射到对应Hash
值的槽位上,然后存储在Hash
表中。- 如果多个都映射到一个相同的槽位上,他们之间就发生了
Hash
冲突,可以通过链表来解决。
【特点】
Hash
索引只能用于等值比较 (=, in
),不支持范围查询 (between, >, <
)- 无法利用索引完成排序操作
- 查询效率高,在没有
Hash
冲突的情况下只需要一次索引
2.4 索引的分类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中的主键创建的索引 | 默认自动创建,唯一 | PRIMARY |
唯一索引 | 避免同一个表中某字段的值重复 | 可有多个 | UNIQUE |
常规索引 | 快速定位数据 | 可有多个 | |
全文索引 | 查找文本中的关键字,而不是索引中的值 | 可有多个 | FULLTEXT |
在 innoDB
存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 | 含义 | 特点 |
---|---|---|
聚簇索引 ( Clutered Index ) |
将数据与索引存放在一起, 索引的叶子节点保存了行数据 |
必须有, 且只能有一个 |
非聚簇索引 ( Secondary Index ) |
将数据与索引分开存放, 索引结构的叶子结点关联的是对应行的主键 |
可以存在多个 |
【聚簇索引的选取规则】
- 如果有主键,主键索引就是聚簇索引
- 如果没有主键,将使用第一个非空索引作为聚簇索引
- 如果以上两者都不满足,
innoDB
会自动生成一个rowid
作为隐藏的聚簇索引。
聚簇索引与非聚簇索引的区别
- 聚簇索引:在
B+ Tree
的叶子结点上保存的数据是行数据
- 非聚簇索引,又叫二级索引。顾名思义,二级索引就是需要索引两次才能找到对应的行数据
row
。即,在非聚簇索引中,B+ Tree
的叶子结点上保存的数据是主键 / 第一个非空索引 / 自动生成的rowid
(会表查询)。
例如:
1 | select * from user where name='Amy'; |
在如上的 SQL
语句中,我们需要
(1)先通过非聚簇索引查找 name = 'Amy'
,得到其对应的 id
(2)再通过聚簇索引查找 id = 3
,得到相应的行数据。