一、MySQL 基础

1. MySQL 概述

数据库相关概念

数据库DataBase)是存储数据的仓库。

进行管理数据库的软件叫做数据库管理系统DataBase Management System)。

操作关系型数据库的编程语言叫做SQLStructured Query Language) 语言,它定义了一套操作关系型数据库的统一标准。

当前比较主流的关系型数据库管理系统:Oracle、MySQL、Microsoft SQL Server….等等。

数据库系统》数据库管理系统》数据库

数据库的下载与安装

MySQL的版本有商业版本与社区版本,其社区免费版本,但不提供技术支持。

MySQL 下载链接

如果之前安装过,需要卸载才能进行安装。

MySQL启动与关闭命令

1
net start mysql80
1
net stop mysql80

MySQL 连接

1
mysql [-h 127.0.0.1] [-p 3306] -u root -p

MySQL 数据模型

  • 关系型数据库(RDBMS)

    概念:建立在关系模型基础上,由多张相互连接的二维表组成的数据库。

    特点

    1. 使用表存储数据,格式统一,便于维护。
    2. 使用SQL语言操作,标准统一,使用方便。

2. SQL

SQL 通用语法

  1. SQL语句可以单行或多行书写,以分号结尾;
  2. SQL语句可以用空格或缩进增强可读性,可以是一个或多个;
  3. SQL语句可大写小写,推荐关键字大写;
  4. 注释:
    • 单行注释:**– 注释内容** 或 # 注释内容(MySQL特有)
    • 多行注释:**/* 注释内容 /*

SQL 分类

分为四类

分类 全称 说明
DDL Data Definition Language 数据定义说明,用来定义数据库对象(数据库、表、字段)
DML Data Manipulation Language 数据操作语言,用来对数据表中的数据进行增删改
DQL Data Query Language 数据查询语言,用来查询数据库中表的记录
DCL Data Control Language 数据控制语言,用来创建数据库用户、控制数据库的访问权限

DDL 语言

DDL 数据库操作
  1. 查询

    • 查询所有数据库
    1
    SHOW DATABASE;
    • 查询当前数据库
    1
    SELECT DATABASE();
    • 查询所有数据库表
    1
    SELECT * FROM 表名;
  2. 创建

    IF NOT EXISTS判断是否有同名,如没有则创建。

    1
    CREATE DATABASE [IF NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [COLLATE 排序规则];
  3. 删除

    1
    DROP DATABASE[IF EXISTS] 数据库名;
  4. 使用

    1
    USE 数据库名;
DDL 表操作-查询
  1. 查询所有数据库表

    1
    SHOW TABLES;
  2. 查询表结构

    1
    DESC 表名;
  3. 查询指定表的建表语句

    1
    SHOW CREATE TABLE 表名;
DDL 表操作-创建
1
2
3
4
5
6
7
8
CREATE TABLE 表名 (
字段1 字段1类型[COMMENT '字段1注释'],
字段2 字段2类型[COMMENT '字段2注释'],
字段3 字段3类型[COMMENT '字段3注释'],
...
字段n 字段n类型[COMMENT '字段3注释']
) [COMMENT '表注释'];
# [...] 为可选
DDL 表操作-数据类型
  1. 数值类型

    类型 大小 有符号(SIGNED)范围 无符号(UNSIGNED)范围 描述
    TINYINT 1 Byte (-128, 127) (0, 255) 小整数值
    SMALLINT 2 Bytes (-32768, 32767) (0, 65535) 大整数值
    MEDIUMINT 3 Bytes (-8388608, 8388607) (0, 16777215) 大整数值
    INT 或 INTEGER 4 Bytes (-2147483648, 2147483647) (0, 4294967295) 大整数值
    BIGINT 8 Bytes (-2^63, 2^63-1) (0, 2^64-1) 极大整数值
    FLOAT 4 Bytes (-3.402823466 E+38, 3.402823466351 E+38) (0, 1.175494351 E-38l, 3.402823466 E+38) 单精度浮点数值
    DOUBLE 8 Bytes (-1.7976931348623157 E+308, 1.7976931348623157 E+308) (0, 2.2250738585072014 E-308, 1.7976931348623157 E+308) 双精度浮点数值
    DECIMAL 依赖M(精度) 和 D(标度)的值 小数值(精确定点数)
    • DECIMAL 长度为精度,小数位数为标度。
  2. 字符串类型

    类型 大小 描述
    CHAR 0~255 Bytes 定长字符串
    VARCHAR 0~65535 Bytes 变长字符串
    TINYBLOB 0~255 Bytes 不超过255个字符的二进制数据
    TINYTEXT 0~255 Bytes 短文本字符串
    BLOB 0~65 535 Bytes 二进制形式的长文本数据
    TEXT 0~65 535 Bytes 长文本数据
    MEDIUMBLOB 0~16 777 215 Bytes 二进制形式的中等长度文本数据
    MEDIUMTEXT 0~16 777 215 Bytes 中等长度文本数据
    LONGBLOB 0~4 294 967 295 Bytes 二进制形式的极大文本数据
    LONGTEXT 0~4 294 967 295 Bytes 极大文本数据
    • 定长指为定死的值,类似常量,若内容长度不符合其指定长度则用空格来补充至指定长度;
    • 变长随着数值内容变化。
  3. 日期类型

    类型 大小 范围 格式 描述
    DATE 3 1000-01-01 至 9999-12-31 YYYY-MM-DD 日期值
    TIME 3 -838:59:59 至 838-59-59 HH:MM:SS 时间值或持续时间
    YEAR 1 1901 至 2155 YYYY 年份值
    DATETIME 8 1000-01-01 00:00:00 至 9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
    TIMESTANP 4 1970-01-01 00:00:01 至 2038-01-19 03:14:07 YYYY-MM-DD HH:MM:SS 混合日期和时间值,时间戳
DDL 表操作-修改
  1. 添加字段

    1
    ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT '注释'] [约束];
  2. 修改字段

    1. 修改数据类型

      1
      ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);
    2. 修改字段名和字段类型

      1
      ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT '注释'] [约束];
  3. 删除字段

    1
    ALTER TABLE 表名 DROP 字段名;
  4. 修改表名

    1
    ALTER TABLE 表名 RENAME TO 新表名; 
  5. 删除表

    1
    DROP TABLE [IF EXISTS] 表名;
    1
    TRUNCATE TABLE 表名; # 删除表并重新建立该表
    • 在删除表时,其表记录也将被删除。

DML 语言

DML语言用来对数据库表的数据记录进行增查删改。

DML 添加数据
  1. 给指定字段添加数据

    1
    INSERT INTO 表名(字段1, 字段2, ...) VALUES(值1, 值2, ...);
  2. 给全部字段添加数据

    1
    INSERT INTO 表名 VALUES(值1, 值2, ...);
  3. 批量字段添加数据

    1
    INSERT INTO 表名(字段1, 字段2, ...) VALUES(值1, 值2, ...),(值1, 值2, ...);
    1
    INSERT INTO 表名 VALUES(值1, 值2, ...),(值1, 值2, ...);
  4. 注意

    • 插入数据时,指定的字段顺序需要与值的顺序一致。
    • 字符串和日期型数据应该包含在引号内。
    • 插入的数据大小,应该在字段的规定范围内。
  5. 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    insert into emp(id, workno, name, gender, age, idcard, workaddress, entrydate)
    values (1, '1', '张三', '女', '19', '123456789012345678', '天津', '2003-08-19'),
    (2, '2', '李四', '男', '23', '123456789012345678', '北京', '2003-03-29'),
    (3, '3', '张大头', '女', '18', '123456789012345678', '江西', '2003-11-28'),
    (4, '4', '筱婕薇', '男', '18', '123456789012345678', '重庆', '2003-08-19'),
    (5, '5', '王婷', '女', '19', '123456789012345678', '江西', '2002-05-11'),
    (6, '6', '张三丰', '男', '43', '123456789012345678', '云南', '1998-03-29'),
    (7, '7', '李旎', '女', '18', '123456789012345678', '四川', '2003-01-19'),
    (8, '8', '张思', '女', '25', null, '广东', '1984-03-29');
DML 修改数据
1
UPDATE 表名 SET 字段名1 = 值1, 字段2 = 值2, ... [WHERE 条件]; # 条件为空则全局修改 
DML 删除数据
1
DELETE FROM 表名 [WHERE 条件] # 缺省条件则删除全部数据
  • DELETE 不能删除字段值,若要删除字段值可以用UPDATE设置为Null

DQL 语言

DQL语言是数据查询语言,用来查询数据库中表的记录。

DQL 语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT 
# 字段列表
FROM
# 表名列表
WHERE
# 条件列表
GROUP BY
# 分组字段列表
HAVING
# 分组后条件列表
ORDER BY
# 排序字段列表
LIMIT
# 分页参数
DQL 基础查询
  1. 查询多个字段

    1
    2
    SELECT 字段1,字段2,字段3,... FROM 表名;
    SELECT * FROM 表名; # * 代表通配符,则查询所有字段
  2. 设置别名

    1
    2
    SELECT 字段1 [AS 别名1],字段2 [AS 别名2], ... FROM 表名;
    SELECT 字段1 '别名1' FROM 表名; # AS 可以省略
  3. 去重查询

    1
    SELECT DISTINCT 字段列表 FROM 表名;
DQL 条件查询
  1. 语法

    1
    SELECT 字段列表 FROM 表名 WHERE 条件列表;
  2. 条件

    比较运算符 说明
    > 大于
    >= 大于等于
    < 小于
    <= 小于等于
    = 等于
    <> 或 != 不等于
    BETWEEN…AND… 在某个范围之内(含最小,最大值)
    IN(…) 在in之后的列表中的值,多选一
    LIKE 占位符 模糊匹配(_匹配单个字符,%匹配任意个字符)
    IS NULL 是NULL
    IS NOT NULL 不是空

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 1.查询年龄为空的数据
    SELECT * FROM Student WHERE age IS NULL;
    # 2.查询年龄不为空的数据
    SELECT * FROM Student WHERE age IS NOT NULL;
    # 3.查询年龄为 18,20,25的数据
    SELECT * FROM Student WHERE age in(18,20,25);
    # 4.查询姓名为两个字的数据
    SELECT * FROM Student WHERE name LIKE '__';
    # 5.查询姓名最后一个字为 筱 的数据
    SELECT * FROM Student WHERE name LIKE '%筱'
    # 6.查询年龄在10~18岁区间(包含)的数据
    SELECT * FROM Student WHERE age BETWEEN 10 AND 18;
    逻辑运算符 说明
    AND 或 && 并且(多个条件同时成立)
    OR 或 || 或者(多个条件满足一个)
    NOT 或 ! 非,不是
DQL 聚合函数
  1. 介绍

    将一列数据作为一个整体,进行纵向运算。

  2. 常见聚合函数

    函数 说明
    MAX 最大值
    MIN 最小值
    COUNT 统计数量
    AVG 平均数
    SUM 求和
  3. 语法

    1
    SELECT 聚合函数(字段列表) FROM 表名;
  4. 注意

    1. NULL值不参与所有聚合运算。
DQL 分组查询
  1. 语法

    1
    SELECT 字段列表 FROM 表名 [WHERE 条件列表] GROUP BY 分组字段名 [HAVING 分组过后的过滤条件];
  2. WHEREHAVING之间的区别

    1. 执行时机不同,WHERE是分组之前的过滤,不满足WHERE条件不参与分组,HAVING是分组之后对结果的过滤;
    2. 判断条件不同,WHERE不能对聚合函数进行判断,HAVING可以。
  3. 注意

    1. 执行顺序:WHERE > HAVING
    2. 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
DQL 排序查询
  1. 语法

    1
    SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式, 字段2 排序方式2 ...;
  2. 排序方式

    1. 升序:ASC
    2. 降序:DESC;
  3. 注意

    如果是多字段排序,则当第一个字段值相同时,才会根据第二个字段排序。

  4. 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 1.以年龄为降序
    select *
    from emp
    order by age desc;

    # 2.根据入职时间,对员工进行降序排序
    select *
    from emp
    order by entrydate desc;

    # 3.根据年龄升序排序,如果年龄相同,在按照入职时间排序
    select *
    from emp
    order by age asc, entrydate desc;
DQL 分页查询
  1. 语法

    1
    SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;
  2. 例子

    1
    2
    3
    4
    5
    6
    # 分页查询
    # 1.查询第1页员工数据,每页展示3条记录
    select * from emp limit 0, 3;

    # 2.查询第2页员工数据,每页展示3条记录 >>>> 计算公式: 页码 - 1 * 记录数 >>>> (2 - 1) * 3
    select * from emp limit 3, 3;
  3. 注意

    1. 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数;
    2. 分页查询是数据库的方言,不同的数据库有不同的实现,如MySQL是LIMIT
    3. 如果查询的是第一页数据,起始索引以省略,直接简写为 LIMIT 10
DQL 练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# DQL 练习
# 1.查询年龄为18,19,20岁的女员工
select * from emp where age in(18,19,20) && gender = '女';

# 2.查询性别为男,并且年龄在20~40岁(含)以内的姓名为两个字的员工
select * from emp where gender = '男' && age between 20 and 40 && name like '__';

# 3.统计员工表中,年龄小于60岁的,男性员工和女性员工的人数
select gender, count(*) from emp where age < 60 group by gender;

# 4.查询所有年龄小于等于20岁的员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同则按照入职时间降序排序
select * from emp where age <= 20 order by age, entrydate desc;

# 5.查询性别为男,且年龄在10~25岁以内(含)的前五个员工的信息,对查询结果按年龄升序,年龄相同就按入职时间升序排序
select * from emp where gender = '男' && age between 10 and 25 order by age, entrydate limit 0, 5;
DQL 执行顺序

左边编写顺序,右边执行顺序

DCL 语言

DCL 用来管理数据库用户、控制数据库的访问权限。

DCL 管理用户
  1. 查询用户

    1
    2
    USE mysql;
    SELECT * FROM user;
  2. 创建用户

    1
    CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
  3. 修改用户密码

    1
    ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
  4. 删除用户

    1
    DROP USER '用户名'@'主机名';
  5. 注意

    1. 主机名可以用%通配;
    2. 这类SQL开发人员操作比较少,主要是DBA(Database Adminstrator 数据库管理员) 使用。
DCL 权限控制

MySQL中定义了许多权限,但是常用的也就以下几种:

权限 说明
ALL, ALL PRIVILEGES 所有 权限
SELECT 查询数据 权限
INSERT 插入数据 权限
UPDATE 修改数据 权限
ALTER 修改表 权限
DROP 删除表/视图/数据库 权限
CREATE 创建数据库/表 权限
  1. 查询权限

    1
    SHOW GRANTS FOR '用户名'@'主机名';
  2. 授予权限

    1
    GRANT 权限列表 ON 数据库名.数据库表 TO '用户名'@'主机名';
  3. 撤销权限

    1
    REVOKE 权限列表 ON 数据库名.数据库表 FROM '用户名'@'主机名';
  4. 注意

    1. 多个权限之间,使用逗号分隔;
    2. 授权时,数据库名和表名可以使用*进行通配,代表所有。

3. 函数

字符串函数

MySQL中内置了很多字符串函数,常用的几个如下:

函数 说明
CONCAT(S1, S2, S3, … Sn) 字符串拼接,将S1,S2,S3…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从start位置起的第len个长度的字符串

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 函数演示
# concat 字符串拼接
select concat('hello', 'MySQL');

# lower 全部转为小写
select lower('Hello');

# upper 全部转为大写
select upper('hello world');

# lpad 左边填充
select lpad('0', 5, '-');

# rpad 右边填充
select rpad('0', 5, '-');

# TRIM 去掉字符串两边的空格
select trim(' Hello World ');

# substring 字符串截取
select substring('ABCDEFG', 2, 3);

案例:

1
2
# 1.企业员工的工号统一为5位数,目前不满足5位数的全部在前面补充0。比如:1号员工工号应该为00001
update emp set workno = lpad(workno, 5, 0);

数值函数

常见的数值函数如下:

函数 说明
CEIL(x) 向上取整
FLOOR(X) 向下取整
MOD(x,y) 返回x/y的模
RAND() 返回随机数
ROUND(x,y) 求参数x的四舍五入的值,保留y位小数

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 数值函数
# CEIL 向上取整
select CEIL(1.01);

# floor 向下取整
select floor(1.9);

# mod 求模预算
select mod(5, 2);

# RAND 随机值
select rand();

# round 四舍五入
select round(2.234, 1);

案例:

1
2
3
4
5
6
# 1.通过数据库的函数,生成一个六位数的随机验证码
SELECT LPAD(ROUND(RAND() * 1000000, 0), 6, 0) '验证码';

# 2.将身份证号与出生日期拼接
SELECT * FROM emp;
UPDATE emp SET idcard = CONCAT(500233, DATE_FORMAT(emp.entrydate, '%Y%m%d'), 8854);

日期函数

常见的日期函数如下:

函数 说明
CURDATE() 返回当前日期
CURTIME() 返回当前时间
NOW() 返回当前日期时间
YEAR(date) 返回指定date的年份
MONTH(date) 返回指定date的月份
DAY(date) 返回指定date的天数
DATE_ADD(date, INTERVAL expr type) 返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1, date2) 返回起始时间date1和结束时间date2之间的天数

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 日期函数
# curdate() 获取当前日期
select CURDATE();

# curtime() 获取当前时间
select CURTIME();

# now() 获取当前日期时间
select NOW();

# YEAR() / MONTH() / DAY()
select year(now());
select month(now());
select day(now());
select day('2003/08/19');

# date_add 获取间隔expr type 后的时间
select date_add(now(), INTERVAL 10 YEAR); # 往后推10年

# datediff 获取时间差
select datediff('2023-11-28', now()); # 相当于 2023-11-28 - 现在的时间
select datediff(now(), '2023-01-27'); # 相当于 现在的时间 - '2023-01-27'

# 案例: 查询所有员工的入职时间,并根据入职时间天数倒序
select * from emp;
select name, datediff(curdate(), entrydate) as '入职天数' from emp order by 入职天数 desc;

流程函数

常见的流程函数:

函数 说明
IF(value, t, f) 如果value为true,则返回t,否则返回f
IFNULL(value1, value2) 如果value1不为空,返回value1,否则返回value2
CASE WHEN [val1] THEN [res1] …ELSE [default] END 如果val1为true,返回res1,…否则返回default默认值
CASE [expr] WHEN [val1] THEN [res1] … ELSE [default] END 如果expr的值等于val1,返回res1,…否则返回default默认值

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 流程函数
# IF(value, t, f)
select if(1 + 1 = 3, 'yes', 'no');

# IFNULL(value1, value2)
select IFNULL('', 'Default'); # 返回的是空白,并不是Default
select IFNULL(null, 'Default');
# value1为null 则返回value2

# case when then else end
# 需求:查询员工的姓名和工作地址(北京》一线城市,其他》二线城市)
select name, case workaddress when '北京' then '一线城市' when '重庆' then '一线城市' else '二线城市' end as '工作地址'
from emp;

# 案例: 查询班级各个学生的成绩,展示规则如下:
# >= 85 返回优秀,>= 60 返回及格,否则 返回不及格
# 首先创建表
use school;
create table score
(
id int comment '学号',
name varchar(20) comment '姓名',
math int comment '数学',
english int comment '英语',
chinese int comment '语文'
) comment '学生成绩表';
select *
from score;
insert into score(id, name, math, english, chinese)
values (1, '肖德华', 98, 87, 45),
(2, '筱婕薇', 76, 45, 23),
(3, '李广', 23, 45, 66),
(4, '张德华', 13, 65, 96);

# 查询
select name,
(case when math >= 85 then '优秀' when math >= 60 then '及格' else '不及格' end) '数学',
(case when english >= 85 then '优秀' when english >= 60 then '及格' else '不及格' end) '英语',
(case when chinese >= 85 then '优秀' when chinese >= 60 then '及格' else '不及格' end) '语文'
from score;

4. 约束

  1. 概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。

  2. 目的:保证数据库中数据的正确、有效性、完整性。

  3. 分类

    约束 描述 关键字
    非空约束 限制该字段的数据不能为null NOT NULL
    唯一约束 限制该表的所有数据是唯一、不重复的 UNIQUE
    主键约束 主键是一行数据的唯一标志,要求非空且唯一 PRIMARY KEY
    默认约束 保存数据时,如果未指定该字段的值,则采用默认值 DEFAULT
    检查约束(8.0.16版本之后) 保证字段值满足某一个条件 CHECK
    外键约束 用来让两张表的数据之间建立连接,保证数据的一致性和完整性 FOREIGN KEY

    注意:约束是作用于表字段的,可以在创建表或修改表时添加约束。

案例:

创建一个表,且规则如下:

字段名 字段含义 字段类型 约束条件 约束关键字
id ID唯一标志 int 主键,并且自动增长 PRIMARY KEY, AUTO_INCREMENT
name 姓名 varchar(10) 不为空,且唯一 NOT NULL, UNIQUE
age 年龄 int 大于0,且小于等于120 CHECK
status 状态 char(1) 如果没指定改值,则默认为1 DEFAULT
gender 性别 char(1)

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 约束
# 创建表 并建立约束
create table user
(
id int primary key auto_increment comment 'ID', # 开启了自动增加,在插入数据时可不用插入
name varchar(10) not null unique comment '姓名',
age int check (age > 0 and age <= 120) comment '年龄',
status char(1) default '1' comment '状态',
gender char(1) comment '性别'
) comment '用户表';

# 测试约束
insert into user(name, age, status, gender)
values ('李依依', 20, '0', '男');

当违反约束则报错,但如果添加了主键增长auto_increment时,会继续自增。

外键约束

外键用来让两张表的数据之间进行数据连接,从而保证数据的一致性和完整性。

添加外键语法:

1
2
3
4
5
create table 表名(
字段名 数据类型,
....
[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
);
1
alter table 表名 add CONSTRAINT 外键名称 FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名);

删除外键:

1
alter table 表名 drop foreign key 外键名称;

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 首先创建两张表再进行相连
# 第一张表
create table dept
(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '部门名称'
) comment '部门表';
# 插入数据
insert into dept (id, name)
VALUES (1, '研发部'),
(2, '市场部'),
(3, '财务部'),
(4, '销售部'),
(5, '总经办');

# 第二张表
create table emp
(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '姓名',
age int comment '年龄',
job varchar(20) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直属领导ID',
dept_id int comment '部门ID'
) comment '员工表';
# 插入数据
insert into emp(id, name, age, job, salary, entrydate, managerid, dept_id)
values (1, '张大头', 66, '总裁', 20000, '2000-01-01', null, 5),
(2, '肖德华', 20, '项目经理', 12500, '2005-12-05', 1, 1),
(3, '张小小', 33, '开发', 8400, '2000-11-03', 2, 1),
(4, '李婷', 48, '开发', 11000, '2002-02-05', 2, 1),
(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),
(6, '张庭峰', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1);

# 建立外键关联
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);

# 测试
delete
from dept
where id = 1; # 将会报错,因为多个字表与父表相连

# 删除外键
alter table emp
drop foreign key fk_emp_dept_id;

外键约束的删除/更新行为:

行为 说明
NO ACTION 当在父表中做删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与RESTRICT一致)
RESTRICT 当在父表中做删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与NO ACTION一致)
CASCADE 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在字表中的记录。
SET NULL 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则设置字表中该外键值为null(这要求该外键允许取null)
SET DEFAULT 父表有变更时,子表将外键列设置成一个默认的值(Innodb不支持)

语法:

1
alter table 表名 add constraint 外键名称 foreign key (外键字段) references 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为; # 指在删除时什么行为,更新时什么行为

例子:

1
2
3
4
5
6
7
8
9
# 外键行为
alter table emp
add constraint fk_emp_dept_id foreign key (dept_id) references dept (id) on update cascade on delete NO ACTION; # 这里设置了更新行为为`cascade` 删除行为为`no active`
# 修改父表
update dept set id = 6 where id = 1;
# 查看子表,会发现数据与父表一致
select * from emp;
# 删除行为
delete from dept where id = 6; # 若设置了删除行为,则根据删除行为`NO ACTION`会拒绝删除

5. 多表查询

概述:在项目开发中,再进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本分为三种:一对多(多对一),多对多,一对一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 多表查询使用数据
# 创建公司数据库
create database company;
use company;

# 创建部门表
create table dept
(
id int auto_increment primary key comment '主键ID',
name varchar(20) comment '部门名称'
) comment '部门表';

# 插入数据
insert into dept(id, name)
VALUES (null, '研发部'),
(null, '市场部'),
(null, '财务部'),
(null, '销售部'),
(null, '总经办'),
(null, '人事部');

# 创建员工信息表 其中 dept_id 外键关联部门表的主键
create table emp
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '员工姓名',
age int unsigned comment '员工年龄',
job varchar(20) comment '员工职位',
salary int unsigned comment '员工薪资',
entrydate date comment '入职时间',
managerid int comment 'managerid',
dept_id int comment '部门ID',
constraint fk_dept_id foreign key (dept_id) references dept (id)
) comment '员工信息表';

# 插入数据
insert into emp(id, name, age, job, salary, entrydate, managerid, dept_id)
values (null, '张大头', 19, '总裁', 20000, '2000-01-01', null, 5),
(null, '筱婕薇', 19, '项目经理', 12500, '2005-01-01', 1, 1),
(null, '张无忌', 20, '开发', 12500, '2005-01-01', 2, 1),
(null, '杨晓', 23, '开发', 12500, '2002-01-01', 2, 1),
(null, '筱婕薇', 19, '项目经理', 12500, '2005-01-01', 1, 1),
(null, '小赵', 19, '程序员鼓励师', 6600, '2004-10-22', 2, 1),
(null, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3),
(null, '周芷若', 19, '会计', 48000, '2006-06-12', 7, 3),
(null, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3),
(null, '赵明', 20, '市场部总监', 12400, '2004-10-12', 1, 2),
(null, '鹿杖客', 56, '职员', 3567, '2006-10-03', 10, 2),
(null, '和顾欣', 19, '职员', 3750, '2007-05-09', 10, 2),
(null, '智彦宇', 19, '职员', 3750, '2009-02-12', 10, 2),
(null, '房东白', 19, '职员', 3750, '2009-02-12', 10, 2),
(null, '张三定', 40, '销售总监', 14000, '2004-01-12', 1, 4),
(null, '俞莲舟', 38, '销售', 4600, '2004-03-21', 14, 4),
(null, '陈丽怡', 23, '销售', 4600, '2006-02-12', 14, 4),
(null, '林亦汐', 32, null, 2000, '2011-10-12', 1, null);

笛卡尔积:笛卡尔乘积是指在数学中,两个集合A集合和B集合的所有组合情况。(在多表查询时,需要消除无效的笛卡尔积)

1
2
3
4
5
# 多表查询--笛卡尔积
select * from emp, dept; # 执行结果会发现是集合呈现组合情况

# 消除无效的笛卡尔积
select * from emp, dept where emp.dept_id = dept.id order by emp.id asc;

多表查询的分类:

  1. 连接查询
    • 内连接:相当于查询A,B交集部分数据;
    • 外连接:
      • 左外连接:查询左表所有数据,以及两张表交集部分数据;
      • 右外连接:查询右表所有数据,以及两张表交集部分数据;
    • 自连接:当前表与自身的连接查询,自连接必须使用表别名。
  2. 子查询

连接分类

内连接

内连接查询语法:

  • 隐式连接:

    1
    select 字段列表 from 表1, 表2 where 条件...;

    例子:

    1
    2
    3
    4
    5
    6
    # 查询每一个员工的姓名,及关联的部门名称(隐式内连接实现)
    # 表结构:emp, dept;
    # 连接条件:emp.dept_id = dept.id;
    select emp.name, dept.name from emp, dept where emp.dept_id = dept.id;
    # 使用别名来简化操作,起了别名就不能使用表名
    select e.name, d.name from emp e, dept d where e.dept_id = d.id;
  • 显式连接:

    1
    select 字段列表 from 表1 [INNER] join 表2 on 连接条件...;

    例子:

    1
    2
    3
    4
    5
    # 查询员工姓名及部门名称 ---- INNER JOIN ... ON ...
    # 表结构:emp, dept;
    # 连接条件:emp.dept_id = dept.id;
    select e.name, d.name from emp e inner join dept d on d.id = e.dept_id;
    # inner 可省略

外连接

外连接查询语法:

  • 左外连接:

    1
    select 字段列表 from 表1 LEFT [OUTER] JOIN 表2 ON 条件...;

    相当于查询表1(左表)的所有数据,且包含表1和表2的交集数据

    例子:

    1
    2
    # 1.查询emp表的所有数据,和对应的部门信息
    select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id; # 显示了左边表的所有数据
  • 右外连接:

    1
    select 字段列表 from 表1 RIGHT [OUTER] JOIN 表2 ON 条件...;

    相当于查询表2(右表)的所有数据,且包含表1和表2的交集数据

    1
    2
    # 1.查询dept表的所有数据,和对应的员工信息
    select d.*, e.* from emp e right join dept d on e.dept_id = d.id; # 显示了右边表的所有数据

自连接

自连接查询语法:

1
select 字段列表 from 表A 别名A join 表A 别名B on 条件...;

自连接查询,可以是内连接查询,也可以是外连接查询。

例子:

1
2
3
4
5
6
# 1.查询员工与所属领导的数据
# 表结构: emp.id, emp.managerid;
# 表条件:emp.id = emp.managerid;
select e1.name '员工姓名', e1.job '员工职位', e2.name '领导姓名', e2.job '领导职位' from emp as e1 join emp as e2 on e1.managerid = e2.id order by e2.job asc;
# 第二种方法
select e1.name '员工姓名', e1.job '员工职位', e2.name '领导姓名', e2.job '领导职位' from emp e1, emp e2 where e1.managerid = e2.id order by e2.job asc;
1
2
3
4
# 2.查询所有员工emp及其领导的名字emp,如果员工没有领导,也需要查询出来
# 表结构: emp a, emp b;
select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid = b.id;
# 因为总裁没有领导所以为空,此时结果输出有领导

多表查询的关系

一对多(多对一)

案例:部分与员工的关系

关系:一个部门对应多个员工,一个员工对应一个部门

实现:在多的一方建立外键,指向一方的主键

外键对主键

多对多

案例:学生与课程的关系

关系:一个学生可以选择多个课程,一个课程可以供多个学生选择

实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

中间表维护两者的关系

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 多表查询
# 多对多
# 创建学生表
create table student
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
# 插入数据
insert into student
values (null, '张大头', '200100101'),
(null, '筱婕薇', '200100102'),
(null, '吴哎一', '200100103'),
(null, '李一丁', '200100104');

# 创建课程表
create table course
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
# 插入数据
insert into course
values (null, 'Java'),
(null, 'MySQL'),
(null, 'C++'),
(null, 'C');

# 创建中间表
create table student_course
(
id int auto_increment comment '主键' primary key,
studentid int not null comment '学生ID',
courseid int not null comment '课程ID',
constraint fk_courseid foreign key (courseid) references course (id),
constraint fk_studentid foreign key (studentid) references student (id)
) comment '学生课程中间表';
# 插入数据
insert into student_course
values (null, 1, 1),
(null, 1, 2),
(null, 1, 3),
(null, 2, 2),
(null, 2, 3),
(null, 3, 4);

可视化视图:

多对多可视化视图

一对一

案例:用户与用户详情的关系

关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率

实现:在任意一方加入外键,关联另外一方的主键,并且设置主键为唯一的(UNIQUE)

一对一关系