1. 约束分类
1.1 约束介绍
约束:对表中的数据进行限定,保证数据的正确性、有效性、完整性
约束的分类:

1.2 主键约束
java
1.主键约束默认包含 非空 和 唯一 两个功能
2.一张表只能有一个主键
3.主键一般用于表中数据的唯一标识
建表时添加主键约束
sql
CREATE TABLE 表名(
列名 数据类型 PRIMARY KEY,
列名 数据类型,
...
);
删除主键约束
sql
ALTER TABLE 表名 DROP PRIMARY KEY;
建表后添加主键约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型 PRIMARY KEY;
sql
-- 创建student表
CREATE TABLE student(
id INT PRIMARY KEY -- 给id添加主键约束
);
-- 添加数据
INSERT INTO student VALUES (1),(2);
-- 主键默认唯一,添加重复数据,会报错
INSERT INTO student VALUES (2);
-- 主键默认非空,不能添加null的数据
INSERT INTO student VALUES (NULL);
1.3 主键自增
主键自增约束可以为空,并自动增长。删除某条数据不影响自增的下一个数值,依然按照前一个值自增
建表时添加主键自增约束
sql
CREATE TABLE 表名(
列名 数据类型 PRIMARY KEY AUTO_INCREMENT,
列名 数据类型,
...
);
删除主键自增约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型;
建表后单独添加主键自增约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型 AUTO_INCREMENT;
sql
-- 创建student2表
CREATE TABLE student2(
id INT PRIMARY KEY AUTO_INCREMENT -- 给id添加主键自增约束
);
-- 添加数据
INSERT INTO student2 VALUES (1),(2);
-- 添加null值,会自动增长
INSERT INTO student2 VALUES (NULL),(NULL);-- 3,4
1.4 唯一约束
唯一约束:约束不能有重复的数据
建表时添加唯一约束
sql
CREATE TABLE 表名(
列名 数据类型 UNIQUE,
列名 数据类型,
...
);
删除唯一约束
sql
ALTER TABLE 表名 DROP INDEX 列名;
建表后单独添加唯一约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型 UNIQUE;
1.5 非空约束
建表时添加非空约束
sql
CREATE TABLE 表名(
列名 数据类型 NOT NULL,
列名 数据类型,
...
);
删除非空约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型;
建表后单独添加非空约束
sql
ALTER TABLE 表名 MODIFY 列名 数据类型 NOT NULL;
1.6 外键约束
外键约束:让表和表之间产生关系,从而保证数据的准确性
建表时添加外键约束
sql
CREATE TABLE 表名(
列名 数据类型 约束,
...
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名)
);
删除外键约束
sql
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
建表后单独添加外键约束
sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名);
sql
-- 创建user用户表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT, -- id
name VARCHAR(20) NOT NULL -- 姓名
);
-- 添加用户数据
INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四'),(NULL,'王五');
-- 创建orderlist订单表
CREATE TABLE orderlist(
id INT PRIMARY KEY AUTO_INCREMENT, -- id
number VARCHAR(20) NOT NULL, -- 订单编号
uid INT, -- 订单所属用户
CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id) -- 添加外键约束
);
-- 添加订单数据
INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),
(NULL,'hm003',2),(NULL,'hm004',2),
(NULL,'hm005',3),(NULL,'hm006',3);
-- 添加一个订单,但是没有所属用户。无法添加
INSERT INTO orderlist VALUES (NULL,'hm007',8);
-- 删除王五这个用户,但是订单表中王五还有很多个订单呢。无法删除
DELETE FROM USER WHERE NAME='王五';
1.7 外键级联
级联操作:当把主表中的数据进行删除或更新时,从表中有关联的数据的相应操作,包括 RESTRICT、CASCADE、SET NULL 和 NO ACTION
sql
RESTRICT 和 NO ACTION相同, 是指限制在子表有关联记录的情况下, 父表不能更新
CASCADE 表示父表在更新或者删除时,更新或者删除子表对应的记录
SET NULL 则表示父表在更新或者删除的时候,子表的对应字段被SET NULL
级联操作:
添加级联更新
sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON UPDATE [CASCADE | RESTRICT | SET NULL];
添加级联删除
sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON DELETE CASCADE;
同时添加级联更新和级联删除
sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON UPDATE CASCADE ON DELETE CASCADE;
2. 多表设计
2.1 一对一
多表:有多张数据表,而表与表之间有一定的关联关系,通过外键约束实现,分为一对一、一对多、多对多三类
举例:人和身份证
实现原则:在任意一个表建立外键,去关联另外一个表的主键
sql
-- 创建person表
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
NAME VARCHAR(20) -- 姓名
);
-- 添加数据
INSERT INTO person VALUES (NULL,'张三'),(NULL,'李四');
-- 创建card表
CREATE TABLE card(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
number VARCHAR(20) UNIQUE NOT NULL, -- 身份证号
pid INT UNIQUE, -- 外键列
CONSTRAINT cp_fk1 FOREIGN KEY (pid) REFERENCES person(id)
);
-- 添加数据
INSERT INTO card VALUES (NULL,'12345',1),(NULL,'56789',2);

2.2 一对多
举例:用户和订单、商品分类和商品
实现原则:在多的一方,建立外键约束,来关联一的一方主键
sql
-- 创建user表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
NAME VARCHAR(20) -- 姓名
);
-- 添加数据
INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四');
-- 创建orderlist表
CREATE TABLE orderlist(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
number VARCHAR(20), -- 订单编号
uid INT, -- 外键列
CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id)
);
-- 添加数据
INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),(NULL,'hm003',2),(NULL,'hm004',2);

2.3 多对多
举例:学生和课程。一个学生可以选择多个课程,一个课程也可以被多个学生选择
实现原则:借助第三张表中间表,中间表至少包含两个列,这两个列作为中间表的外键,分别关联两张表的主键
sql
-- 创建student表
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
NAME VARCHAR(20) -- 学生姓名
);
-- 添加数据
INSERT INTO student VALUES (NULL,'张三'),(NULL,'李四');
-- 创建course表
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
NAME VARCHAR(10) -- 课程名称
);
-- 添加数据
INSERT INTO course VALUES (NULL,'语文'),(NULL,'数学');
-- 创建中间表
CREATE TABLE stu_course(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
sid INT, -- 用于和student表中的id进行外键关联
cid INT, -- 用于和course表中的id进行外键关联
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id), -- 添加外键约束
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id) -- 添加外键约束
);
-- 添加数据
INSERT INTO stu_course VALUES (NULL,1,1),(NULL,1,2),(NULL,2,1),(NULL,2,2);

3. 连接查询
3.1 内连接
连接查询的是两张表有交集的部分数据,两张表分为驱动表和被驱动表,如果结果集中的每条记录都是两个表相互匹配的组合,则称这样的结果集为笛卡尔积
内连接查询,若驱动表中的记录在被驱动表中找不到匹配的记录时,则该记录不会加到最后的结果集
显式内连接
sql
SELECT 列名 FROM 表名1 [INNER] JOIN 表名2 ON 条件;
隐式内连接:内连接中 WHERE 子句和 ON 子句是等价的
sql
SELECT 列名 FROM 表名1,表名2 WHERE 条件;
3.2 外连接
外连接查询,若驱动表中的记录在被驱动表中找不到匹配的记录时,则该记录也会加到最后的结果集,只是对于被驱动表中不匹配过滤条件的记录,各个字段使用 NULL 填充
应用实例:查学生成绩,也想展示出缺考的人的成绩
左外连接:选择左侧的表为驱动表,查询左表的全部数据,和左右两张表有交集部分的数据
sql
SELECT 列名 FROM 表名1 LEFT [OUTER] JOIN 表名2 ON 条件;
右外连接:选择右侧的表为驱动表,查询右表的全部数据,和左右两张表有交集部分的数据
sql
SELECT 列名 FROM 表名1 RIGHT [OUTER] JOIN 表名2 ON 条件;

3.3 关联查询
自关联查询:同一张表中有数据关联,可以多次查询这同一个表
sql
-- 创建员工表
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(20), -- 员工姓名
mgr INT, -- 上级编号
salary DOUBLE -- 员工工资
);
-- 添加数据
INSERT INTO employee VALUES (1001,'孙悟空',1005,9000.00),..,(1009,'宋江',NULL,16000.00);

sql
-- 查询所有员工的姓名及其直接上级的姓名,没有上级的员工也需要查询
/*
分析
员工信息 employee表
条件:employee.mgr = employee.id
查询左表的全部数据,和左右两张表有交集部分数据,左外连接
*/
SELECT
e1.id,
e1.name,
e1.mgr,
e2.id,
e2.name
FROM
employee e1
LEFT OUTER JOIN
employee e2
ON
e1.mgr = e2.id;
sql
id name mgr id name
1001 孙悟空 1005 1005 唐僧
1002 猪八戒 1005 1005 唐僧
1003 沙和尚 1005 1005 唐僧
1004 小白龙 1005 1005 唐僧
1005 唐僧 NULL NULL NULL
1006 武松 1009 1009 宋江
1007 李逵 1009 1009 宋江
1008 林冲 1009 1009 宋江
1009 宋江 NULL NULL NULL
3.4 连接原理


3.5 连接优化
BKA
sql
Batched Key Access 算法是对 NLJ 算法的优化,在读取被驱动表的记录时使用顺序 IO,Extra 信息中会有 Batched Key Access 信息

BNL

优化

4. 嵌套查询
4.1 查询分类
查询语句中嵌套了查询语句,将嵌套查询称为子查询,FROM 子句后面的子查询的结果集称为派生表
根据结果分类:
结果是单行单列:可以将查询的结果作为另一条语句的查询条件,使用运算符判断
sql
SELECT 列名 FROM 表名 WHERE 列名=(SELECT 列名/聚合函数(列名) FROM 表名 [WHERE 条件]);
结果是多行单列:可以作为条件,使用运算符 IN 或 NOT IN 进行判断
sql
SELECT 列名 FROM 表名 WHERE 列名 [NOT] IN (SELECT 列名 FROM 表名 [WHERE 条件]);
结果是多行多列:查询的结果可以作为一张虚拟表参与查询
sql
SELECT 列名 FROM 表名 [别名],(SELECT 列名 FROM 表名 [WHERE 条件]) [别名] [WHERE 条件];
-- 查询订单表orderlist中id大于4的订单信息和所属用户USER信息
SELECT
*
FROM
USER u,
(SELECT * FROM orderlist WHERE id>4) o
WHERE
u.id=o.uid;
4.2 查询优化

4.3 联合查询
UNION 是取这两个子查询结果的并集,并进行去重,同时进行默认规则的排序(union 是行加起来,join 是列加起来)
UNION ALL 是对两个结果集进行并集操作不进行去重,不进行排序
sql
(select 1000 as f) union (select id from t1 order by id desc limit 2); #t1表中包含id 为 1-1000 的数据
