- 基本概念
DBMS 和 RDBMS
发展历史
关系型数据库和非关系型数据库
2.ER模型与表记录的4种关系:
表与表之间的数据记录有关系(relationship)。现实世界中的各种实体以及实体之间的各种联系均用
关系模型来表示。
一对一: 这种通常将所有数据放在一张表中
一对多: 一个用户有多个订单(客户和订单是一对多的关系)、一个部门多个员工
多对多: 要表示多对多关系,必须创建第三个表,该表通常称为联接表,它将多对多关系划分为两个一对多关
系。将这两个表的主键都插入到第三个表中。
例子: 学生与课程就是多对多: 一个学生可以选多门课, 一门课可以被多个学生选择
选课信息表作为中间表,记录了每个学生的选课信息
订单与产品是多对多: 一个订单可以有多个产品,每个产品可以出现在多个订单中
订单明细表就是中间表
自我引用: 自己引用自己
例子: 员工表中,有员工id,Managerid,一个人他可能即是员工又是管理者,
Managerid是103的记录和员工id是103的记录,之间就是自我引用
- 基本的Select语句
1)SQL语句分类:
- DDL(Data Definition Languages、数据定义语言),这些语句定义了不同的数据库、表、视图、索
引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。 --> 对库和表结构的的定义、增删改
主要的语句关键字包括CREATE 、DROP 、ALTER 等。 - DML(Data Manipulation Language、数据操作语言),用于添加、删除、更新和查询数据库记
录,并检查数据完整性。
主要的语句关键字包括INSERT 、DELETE 、UPDATE 、SELECT 等。
SELECT是SQL语言的基础,最为重要。 --> 对数据的增删改查 - DCL(Data Control Language、数据控制语言),用于定义数据库、表、字段、用户的访问权限和
安全级别。
因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类:DQL(数据查询语言)。
还有单独将COMMIT 、ROLLBACK 取出来称为TCL (Transaction Control Language,事务控制语
言)。
- 列取别名:
as:全称:alias(别名),可以省略
列的别名可以使用一对""引起来,不要使用''。
SELECT employee_id emp_id,last_name AS lname,department_id "部门id",salary * 12 AS "annual sal"
FROM employees;
- 去除重复行
DISTINCT
SELECT DISTINCT department_id
FROM employees;
两点:
DISTINCT放到所有列名的最前面.
ISTINCT 其实是对后面所有列名的组合进行去重.
- 空值参与运算
所有运算符或列值遇到null值,运算的结果都为null
5)使用WHERE过滤数据
#练习:查询90号部门的员工信息
SELECT *
FROM employees
#过滤条件,声明在FROM结构的后面
WHERE department_id = 90;
- 运算符
- 算术运算符: + - * / div % mod
- 比较运算符
2.1 = <=> <> != < <= > >=
2.2 IS NULL \ IS NOT NULL \ ISNULL
2.3 BETWEEN 条件下界1 AND 条件上界2 (查询条件1和条件2范围内的数据,包含边界)
2.4 LIKE 模糊查询
"%":匹配0个或多个字符。
"_":只能匹配一个字符。
#练习:查询last_name中包含字符'a'的员工信息
SELECT last_name
FROM employees
WHERE last_name LIKE '%a%';
_ :代表一个不确定的字符
#练习:查询第3个字符是'a'的员工信息
SELECT last_name
FROM employees
WHERE last_name LIKE '__a%';
2.5 REGEXP :正则表达式
(1)'^'匹配以该字符后面的字符开头的字符串。 ^a 匹配以a开头的字符串
(2)''匹配以该字符前面的字符结尾的字符串。 a 匹配以a结尾的字符串
2.6 逻辑运算符: OR || AND && NOT ! XOR
2.7 位运算符: & | ^ ~ >> <<
- 排序与分页
- 使用 ORDER BY 对查询到的数据进行排序操作。
升序:ASC (ascend)
降序:DESC (descend)
练习:按照salary从高到低的顺序显示员工信息
SELECT employee_id,last_name,salary
FROM employees
ORDER BY salary DESC; // 默认按照升序排列
使用多列进行排序:
#练习:显示员工信息,按照department_id的降序排列,salary的升序排列
SELECT employee_id,salary,department_id
FROM employees
ORDER BY department_id DESC,salary ASC;
A.可以使用不在SELECT列表中的列排序。
B.在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第
一列数据中所有值都是唯一的,将不再对第二列进行排序。
2)LIMIT 用于分页
语法:
LIMIT [从哪开始返回,] 返回多少行
MySQL 8.0中的语法:
LIMIT 返回多少行 OFFSET 从哪开始返回
需求1:每页显示20条记录,此时显示第1页
SELECT employee_id,last_name
FROM employees
LIMIT 0,20;
#需求:每页显示pageSize条记录,此时显示第pageNo页:
#公式:LIMIT (pageNo-1) * pageSize,pageSize;
注意:
ORDER BY 和 LIMIT应该位于在整个SELECT语句的最后.
LIMIT 应该在ORDER BY 的后面
-
多表查询
-
单行函数
这章可以跳过,讲的是MySQL里面提供的一些函数,用的时候再查。每个函数
PDF里面有详细的介绍
函数分类: 单行函数与多行函数
单行函数:只对一行进行变换, 每行返回一个结果
多行函数:对多行进行变换, 整体返回一个结果
单行函数有这些:
1)基本函数
求绝对值、随机值等
2)角度与弧度互换函数
3)三角函数
4)指数与对数
5)进制间的转换
6)字符串函数:
LENGTH(s) 返回字符串s的字节数,和字符集有关
CONCAT(s1,s2,...,sn) 连接s1,s2,...,sn为一个字符串
还有REPLACE、INSERT、FIND等
7)日期和时间函数
包括获取当前时间、时间转换、格式化、时间之间的计算等
8)流程控制函数
IF、IFNULL、CASE...WHEN等
IF(value,value1,value2) 如果value的值为TRUE,返回value1,否则返回value2
IFNULL(value1, value2) 如果value1不为NULL,返回value1,否则返回value2
CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2... [ELSE resultn] END -> 相当于Java的if...else if...else...
例子:
SELECT last_name,salary,CASE WHEN salary >= 15000 THEN '白骨精'
WHEN salary >= 10000 THEN '潜力股'
WHEN salary >= 8000 THEN '小屌丝'
ELSE '草根' END "details",department_id
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值1 THEN值1 ... [ELSE 值n] END -> 相当于Java的switch...case...
/*
练习1
查询部门号为 10,20, 30 的员工信息,
若部门号为 10, 则打印其工资的 1.1 倍,
20 号部门, 则打印其工资的 1.2 倍,
30 号部门,打印其工资的 1.3 倍数,
其他部门,打印其工资的 1.4 倍数
*/
SELECT employee_id,last_name,department_id,salary,CASE department_id WHEN 10 THEN salary * 1.1
WHEN 20 THEN salary * 1.2
WHEN 30 THEN salary * 1.3
ELSE salary * 1.4 END "details"
FROM employees;
9)加密与解密函数
密码加解密、md5等
- MySQL信息函数
获取当前MySQL的一些信息,例如版本、连接数等
- 多行函数(聚合函数、组函数、聚集函数)
聚合函数作用于一组数据,并对一组数据返回一个值。
常见的几个聚合函数
AVG、SUM、MAX、MIN、COUNT
1)可以对数值型数据使用AVG 和 SUM 函数。
例子: 对薪水求平均值和求和
SELECT AVG(salary),SUM(salary),AVG(salary) * 107
FROM employees;
2)可以对任意数据类型的数据使用 MIN 和 MAX 函数。
例子: 对薪水求最大、最小值
SELECT MAX(salary),MIN(salary)
FROM employees;
例子: 对last_name、hire_date求最大最小值,hire_date是日期类型、last_name是字符串
SELECT MAX(last_name),MIN(last_name),MAX(hire_date),MIN(hire_date)
FROM employees;
3)COUNT: 计算指定字段在查询结构中出现的个数(不包含NULL值的,如果指定的字段是NULL,则这一行不会被统计)
例子:
SELECT COUNT(commission_pct)
FROM employees;
#需求:查询公司中平均奖金率
#错误的!因为commission_pct有NULL值,并不是所有人都有奖金,AVG只会计算那些有奖金的人的平均值
而题目要求的是所有人的平均奖金,应该是有奖金的人的奖金率 除以 公司总人数(而不是有奖金的人数)
SELECT AVG(commission_pct)
FROM employees;
#正确的:
SELECT SUM(commission_pct) / COUNT(IFNULL(commission_pct,0)),
AVG(IFNULL(commission_pct,0))
FROM employees;
#如果计算表中有多少条记录,如何实现?
#方式1:COUNT()
#方式2:COUNT(1)
#方式3:COUNT(具体字段) : 不一定对!因为count()会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
如何需要统计表中的记录数,使用COUNT(*)、COUNT(1)、COUNT(具体字段) 哪个效率更高呢?
如果使用的是MyISAM 存储引擎,则三者效率相同,都是O(1) ,这种引擎内部有一计数器在维护着行数,直接取值就行
如果使用的是InnoDB 存储引擎,则三者效率:COUNT(*) = COUNT(1)> COUNT(字段)。
4)GROUP BY
用于将表中的数据分成若干组
1)在SELECT列表中所有未包含在组函数中的列都应该包含在GROUP BY子句中
2)GROUP BY 声明在FROM后面、WHERE后面,ORDER BY 前面、LIMIT前面
使用单个列分组:
#需求:查询各个部门的平均工资,最高工资
SELECT department_id,AVG(salary),SUM(salary)
FROM employees
GROUP BY department_id;
使用多个列分组:
#需求:查询各个department_id,job_id的平均工资
#方式1:
SELECT department_id,job_id,AVG(salary)
FROM employees
GROUP BY department_id,job_id;
5)HAVING
对分组之后的数据进行进一步过滤。
#要求1:如果过滤条件中使用了聚合函数,则必须使用HAVING来替换WHERE。否则,报错。 --> 聚合函数不能用在WHERE中
#要求2:HAVING 不能单独使用,必须要跟 GROUP BY 一起使用,HAVING 必须声明在 GROUP BY 的后面。
#要求3:当过滤条件中没有聚合函数时,则此过滤条件声明在WHERE中或HAVING中都可以。但是,建议大家声明在WHERE中。
#练习:查询各个部门中最高工资比10000高的部门信息
#错误的写法:
SELECT department_id,MAX(salary)
FROM employees
WHERE MAX(salary) > 10000
GROUP BY department_id;
#正确的写法:
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000;
6)WHERE和HAVING的对比
1.HAVING必须和ORDER BY一起使用,WHERE不用
2.WHERE中不能使用聚合函数,HAVING可以
3.WHERE的执行顺序比HAVING靠前
4.如果过滤条件中没有聚合函数:这种情况下,WHERE的执行效率要高于HAVING
因为 WHERE 可以先筛选,用一个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。
HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用
的资源就比较多,执行效率也较低。
7)SELECT的执行过程
#SELECT 语句的完整结构
/*
#sql92语法:
SELECT ...,...,...(存在聚合函数)
FROM ...,...,...
WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件
GROUP BY ...,...
HAVING 包含聚合函数的过滤条件
ORDER BY ...,...(ASC / DESC )
LIMIT ...,...
#sql99语法:
SELECT ...,...,...(存在聚合函数)
FROM ... (LEFT / RIGHT)JOIN ...ON 多表的连接条件
(LEFT / RIGHT)JOIN ... ON ...
WHERE 不包含聚合函数的过滤条件
GROUP BY ...,...
HAVING 包含聚合函数的过滤条件
ORDER BY ...,...(ASC / DESC )
LIMIT ...,...
*/
需要记住 SELECT 查询时的两个顺序:
1.1 关键字的顺序是不能颠倒的: SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
1.2 SELECT 语句的执行顺序: FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT --> 先执行中间FROM到HAVING那块,然后执行SELECT,最后执行ORDER BY和LIMIT
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个虚拟表,然后将这个虚拟表传入下一个步
骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
HAVING num > 2 # 顺序 4
ORDER BY num DESC # 顺序 6
LIMIT 2 # 顺序 7
8)SQL执行原理
SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:
-
首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1;
-
通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2;
-
添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟
表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3。
当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得
到是我们的原始数据。
当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 vt1 ,就可以在此基础上再进行 WHERE 阶
段。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 vt2 。
然后进入第三步和第四步,也就是 GROUP 和 HAVING 阶段。在这个阶段中,实际上是在虚拟表 vt2 的
基础上进行分组和分组过滤,得到中间的虚拟表 vt3 和 vt4 。
当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT
阶段。
首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表
vt5-1 和 vt5-2 。
当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段,得到
虚拟表 vt6 。
最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段,得到最终的结果,对应的是虚拟表
vt7 。
当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。
同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的
关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。
-
子查询
-
创建和管理表
-
数据处理之增删改
1)添加数据
方式1: 一条一条的添加数据:
指明要添加的字段 (推荐)
INSERT INTO emp1(id,hire_date,salary,
name
)VALUES(2,'1999-09-09',4000,'Jerry');
方式2: 同时插入多条记录 (推荐)
INSERT INTO emp1(id,NAME,salary)
VALUES
(4,'Jim',5000),
(5,'张俊杰',5500);
方式3: 将查询结果插入到表中
INSERT还可以将SELECT语句查询的结果插入到表中,此时不需要把每一条记录的值一个一个输入,只需
要使用一条INSERT语句和一条SELECT语句组成的组合语句即可快速地从一个或多个表中向一个表中插入
多行。
例子:
INSERT INTO emp1(id,NAME,salary,hire_date)
#查询语句
SELECT employee_id,last_name,salary,hire_date # 查询的字段一定要与添加到的表的字段一一对应
FROM employees
WHERE department_id IN (70,60);
- 更新数据 (或修改数据)
UPDATE ... SET ... WHERE ...
可以实现批量修改数据的。
例子:
修改一恶搞字段:
UPDATE emp1
SET hire_date = CURDATE()
WHERE id = 5;
#同时修改一条数据的多个字段:
UPDATE emp1
SET hire_date = CURDATE(),salary = 6000
WHERE id = 4;
- 删除数据
DELETE FROM ... WHERE...
DELETE FROM emp1
WHERE id = 1;
#小结:DML操作默认情况下,执行完以后都会自动提交数据。
如果希望执行完以后不自动提交数据,则需要使用 SET autocommit = FALSE. --> 设置之后可以通过rollback回滚数据。
4)MySQL8的新特性:计算列
什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,a列值为1、b列值为2,c列
不需要手动插入,定义a+b的结果为c的值,那么c就是计算列,是通过别的列计算得来的。
例子:
CREATE TABLE test1(
a INT,
b INT,
c INT GENERATED ALWAYS AS (a + b) VIRTUAL #字段c即为计算列
);
插入数据,c列的值会自动计算,存储在表中:
INSERT INTO test1(a,b)
VALUES(10,20);
- MySQL数据类型精讲
在定义数据类型时,如果确定是整数,就用INT ; 如果是小数,一定用定点数类型
DECIMAL(M,D) ; 如果是日期与时间,就用 DATETIME 。
本章直接参考课件。
- 约束
/*
- 基础知识
1.1 为什么需要约束? 为了保证数据的完整性!
1.2 什么叫约束?对表中字段的限制。
1.3 约束的分类:
角度1:约束的字段的个数
单列约束 vs 多列约束
角度2:约束的作用范围
列级约束:将此约束声明在对应字段的后面
表级约束:在表中所有字段都声明完,在所有字段的后面声明的约束
角度3:约束的作用(或功能)
① not null (非空约束)
② unique (唯一性约束)
③ primary key (主键约束)
④ foreign key (外键约束)
⑤ check (检查约束)
⑥ default (默认值约束)
1.4 如何添加/删除约束?
CREATE TABLE时添加约束
ALTER TABLE 时增加约束、删除约束
*/
- 非空约束NOT NULL -- 限定某个字段/某列的值不允许为空
默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
一个表可以有很多列都分别限定了非空
空字符串''不等于NULL,0也不等于NULL
在CREATE TABLE时添加约束:
CREATE TABLE test1(
id INT NOT NULL,
last_name VARCHAR(15) NOT NULL,
email VARCHAR(25),
salary DECIMAL(10,2)
);
在ALTER TABLE时添加约束:
ALTER TABLE test1
MODIFY email VARCHAR(25) NOT NULL;
在ALTER TABLE时删除约束:
ALTER TABLE test1
MODIFY email VARCHAR(25) NULL;
- 唯一性约束UNIQUE -- 用来限制某个字段/某列的值不能重复。
同一个表可以有多个唯一约束。
唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
唯一性约束允许列值为空。
在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
MySQL会给唯一约束的列上默认创建一个唯一索引。
在CREATE TABLE时添加约束:
CREATE TABLE test2(
id INT UNIQUE, #列级约束
last_name VARCHAR(15) ,
email VARCHAR(25),
salary DECIMAL(10,2),
#表级约束
CONSTRAINT uk_test2_email UNIQUE(email)
);
在ALTER TABLE时添加约束:
#方式1:
ALTER TABLE test2
ADD CONSTRAINT uk_test2_sal UNIQUE(salary);
#方式2:
ALTER TABLE test2
MODIFY last_name VARCHAR(15) UNIQUE;
复合唯一约束: 多个列组成唯一性约束
例子:
CREATE TABLE USER(
id INT,
name
VARCHAR(15),
password
VARCHAR(25),
#表级约束
CONSTRAINT uk_user_name_pwd UNIQUE(name
,password
)
);
删除唯一性约束
-- 添加唯一性约束的列上也会自动创建唯一索引。
-- 删除唯一约束只能通过删除唯一索引的方式删除。
-- 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
-- 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和()中排在第一个的列名相同。也可以自定义唯一性约束名。
例子:
ALTER TABLE test2
DROP INDEX last_name;
- primary key (主键约束) -- 用来唯一标识表中的一行记录。
主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值。
一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
主键约束对应着表中的一列或者多列(复合主键)
如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询
的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。
需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的
值,就有可能会破坏数据的完整性。
添加主键约束:
CREATE TABLE test4(
id INT PRIMARY KEY, #列级约束
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25)
);
在ALTER TABLE时添加约束:
CREATE TABLE test6(
id INT ,
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25)
);
ALTER TABLE test6
ADD PRIMARY KEY (id);
删除主键约束:
ALTER TABLE test6
DROP PRIMARY KEY;
- 自增长列:AUTO_INCREMENT -- 某个字段的值自增
(1)一个表最多只能有一个自增长列
(2)当需要产生唯一标识符或顺序值时,可设置自增长
(3)自增长列约束的列必须是键列(主键列,唯一键列)
(4)自增约束的列的数据类型必须是整数类型
(5)如果自增列指定了 0 和 null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接
赋值为具体值。
在CREATE TABLE时添加
CREATE TABLE test7(
id INT PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(15)
);
#开发中,一旦主键作用的字段上声明有AUTO_INCREMENT,则我们在添加数据时,就不要给主键
#对应的字段去赋值了。
INSERT INTO test7(last_name)
VALUES('Tom');
#当我们向主键(含AUTO_INCREMENT)的字段上添加0 或 null时,实际上会自动的往上添加指定的字段的数值
INSERT INTO test7(id,last_name)
VALUES(0,'Tom');
INSERT INTO test7(id,last_name)
VALUES(NULL,'Tom');
在ALTER TABLE 时添加:
CREATE TABLE test8(
id INT PRIMARY KEY ,
last_name VARCHAR(15)
);
在ALTER TABLE 时删除:
ALTER TABLE test8
MODIFY id INT;
MySQL 8.0新特性---自增变量的持久化
Mysql5.7中,自增的列,比如id列,自增到了5,如果删除了id为5的列,然后重启mysql server,下一次添加新的数据,id会从5开始,
Mysql8中,id会从6开始。原因如下:
在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典
内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久化到磁盘中。当数据库重启时,该
计数器会被初始化,读取表中的数据,看现在到多少了。
MySQL 8.0将自增主键的计数器持久化到重做日志中。每次计数器发生改变,都会将其写入重做日志
中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。
5)foreign key (外键约束) -- 限定某个表的某个字段的引用完整性。
比如:员工表的员工所在部门的选择,必须在部门表能找到对应的部门。
主表(父表):被引用的表,被参考的表
从表(子表):引用别人的表,参考别人的表
例如:员工表的员工所在部门这个字段的值要参考部门表:部门表是主表,员工表是从表。
例如:学生表、课程表、选课表:选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是
主表,选课表是从表。
(1)从表的外键列,必须引用/参考主表的主键或唯一约束的列
为什么?因为被依赖/被参考的值必须是唯一的
(2)在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如
student_ibfk_1;),也可以指定外键约束名。
(3)创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
(4)删表时,先删从表(或先删除外键约束),再删除主表
(5)当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖
该记录的数据,然后才可以删除主表的数据
(6)在"从表"中指定外键约束,并且一个表可以建立多个外键约束
(7)从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类
型不一样,创建子表时,就会出现错误"ERROR 1005 (HY000): Can't create
table'database.tablename'(errno: 150)"。
例如:都是表示部门编号,都是int类型。
(8)当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是外键的约束
名。(根据外键查询效率很高)
(9)删除外键约束后,必须手动删除对应的索引
创建外键约束:
CREATE TABLE emp1(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(15),
department_id INT,
#表级约束
CONSTRAINT fk_emp1_dept_id FOREIGN KEY (department_id) REFERENCES dept1(dept_id)
);
在ALTER TABLE时添加外键约束:
ALTER TABLE emp2
ADD CONSTRAINT fk_emp2_dept_id FOREIGN KEY(department_id) REFERENCES dept2(dept_id);
约束关系是针对双方的:
添加了外键约束后,主表的修改和删除数据受约束
添加了外键约束后,从表的添加和修改数据受约束
在从表上建立外键,要求主表必须存在
删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除
约束等级:
-- Cascade方式
:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
-- Set null方式
:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
-- No action方式
:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
-- Restrict方式
:同no action, 都是立即检查外键约束
-- Set default方式
(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
如果没有指定等级,就相当于Restrict方式。
对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT 的方式。 也就是更新主表数据的时候同步更新从表,删除主表数据的时候,只有从表没有使用主表的这个数据的时候才能删除成功。
删除外键约束:
(1)第一步先查看约束名和删除外键约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个
表的约束名
ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;
(2)第二步查看索引名和删除索引。(注意,只能手动删除)
SHOW INDEX FROM 表名称; #查看某个表的索引名
ALTER TABLE 从表名 DROP INDEX 索引名;
在 MySQL 里,外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适
合。比如大型网站的中央数据库,可能会因为外键约束的系统开销而变得非常慢。所以, MySQL 允
许你不使用系统自带的外键约束,在应用层面完成检查数据一致性的逻辑。也就是说,即使你不
用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。
阿里开发规范:
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学
生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单
机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响
数据库的插入速度。
6)check 约束 -- 检查某个字段的值是否符号xx要求,一般指的是值的范围
mysql8才支持。
CREATE TABLE test10(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) CHECK(salary > 2000) --> 薪水必须大于2000
);
#1500添加失败
INSERT INTO test10
VALUES(2,'Tom1',1500);
- DEFAULT约束 -- 给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则赋值为默
认值。
在CREATE TABLE添加约束:
CREATE TABLE test11(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) DEFAULT 2000
);
在ALTER TABLE添加约束:
CREATE TABLE test12(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2)
);
在ALTER TABLE删除约束
ALTER TABLE test12
MODIFY salary DECIMAL(8,2);