JavaSE|MySQL 数据库约束、聚合查询、多表查询

一、数据库约束

约束就是数据库在使用的时候,对于里面能够存的数据提出的要求和限制,程序猿就可以借助约束来完成更好的校验
1、约束类型 ① NULL约束
NOT NULL - 指示某列不能存储 NULL 值,插入会直接报错
  • 给id这一列设为not null 之后,此时这里的值就变成NO了
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 当尝试插入空值的时候,就直接会报错,插入失败
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • NOT NULL是可以给任意个列来进行设置,不仅仅是这一个列
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

② UNIQUE:唯一约束
UNIQUE - 保证某列的每行必须有唯一的值,尝试插入也会报错。
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

③ DEFAULT:默认值约束
DEFAULT - 规定没有给列赋值时的默认值。
  • MySQL中,默认的默认值,是 NULL
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 此处也可以把默认的默认值给改成其他咱们需要的内容
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

④ PRIMARY KEY:主键约束
PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
主键约束,相当于数据的唯一身份标识。类似于身份证号码 / 手机号码。
这个是咱们日常开发的时候最常使用的约束!最重要的约束。
创建表的时候,很多时候都需要指定主键。
  • 对于一个表来说,只能有一个列被指定为主键
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 关于主键,典型的用法,就是直接使用1,2,3,4整数递增的方式来进行表示
    MySQL里面对于这种递增的主键,是有内置支持的,称为 "自增主键"
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 当设定好自增主键之后,此时插入的记录,就可以不指定自增主键的值了(直接使用null 来表示),交给 mysql自行分配即可
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 依靠mysql的自增主键来分配的
    每次新增一个新的记录,都会产生一个自增的id
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 自增主键也是可以手动指定id的,一旦指定过之后,后续新插入的数据就都是从10之后来排了3-10之间的就用不了
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

上面说的这些约束,都是针对每个列单独设置的,只是针对这个列来说是这样的,不同的列之间没有影响
⑥ FOREIGN KEY:外键约束
FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
外键用于关联其他表的主键或唯一键
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 构造一个学生表,来指定一下外键约束
    JavaSE|MySQL 数据库约束、聚合查询、多表查询
    文章图片

  • 在student表中插入数据:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 修改 student 表中的 Classid:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 刚才都是针对子表来操作,外键约束同样也在约束父表
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

外键约束的工作原理:
在子表中插入新的记录的时候,就会先根据对应的值,在父表中先查询,查询到之后,才能够执行后续的插入
这里的查询操作,可能是一个成本较高的操作(比较耗时),外键约束其实要求,父表中被依赖的这一列,必须要有索引,有了索引就能大大的提高查询速度~
class 表的 classld 这一列,得是 primary key 或者 unique (有了这俩约束的列,就会自动的创建出索引了)
  • 想象一个场景,购物
商品表 (保存了商品信息) :商品id 商品名字…
订单表 (保存了订单信息) :订单id 商品id …
订单是不应该被删除的!用户随时可能要看看这里的内容
按照上面描述的外键约束的关系
就可以认为,商品表就是父表,订单表就是子表。订单表中的商品id应该是在商品表的商品id中存在
好单,买好东西,很久之后,我发现,这个商品下架了(被从商品表中删除了),这个数据,是如何删除的,
按照刚才说的外键约束,—旦建立好了约束关系,此时父表的记录不能随便删除如果这个记录被子表依赖了,此时就无法删除了~~
采取的办法,不是真正的删除,而是 “逻辑上的删除”
给商品表加一个列 isOk 。如果这—列值为1,表示这是一个有效记录。如果这一列值为0,表示这是一个无效记录(相当于被删除了)
如果不是真的删除掉,而只是逻辑上删除的话,这个记录不是就始终存在嘛?这不就会导致数据库里面的内容越来越多,逐渐膨胀,非常浪费硬盘空间嘛?
硬盘空间,不值钱!远远不如人力成本高
练习:
设计一个学校食堂管理系统
学校食堂管理系统,包含食堂表,食堂仓口表,仓口收费记录表
create table canteen ( id int primary key auto_increment, name varchar(20)); create table canteen_window ( id int primary key auto_increment, name varchar(20), window_id int, foreign key(window_id) references canteen(id)); create table canteen_charge ( id int primary key auto_increment, price int, charge_date datetime, canteen_window_id int, foreign key(canteen_window_id) references canteen_window(id)); insert into canteen_charge values (null, 20, '2022-02-02 12:12:12', 1); insert into canteen_charge values (null, 15, '2022-02-03 13:13:13', 2); insert into canteen_charge values (null, 30, '2022-02-04 15:15:15', 3);

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

设计一个车辆违章系统
车辆违章系统,包含用户表,车辆表,违章信息表。违章信息表中包含用户和车辆的违章信息
用户可以拥有多辆车,关系为1:m,题目已经说明违章信息包括用户和车辆,说明违章信息表中要记录用户和车辆,一个用户可以有多次违章记录,用户与违章记录关系为1:m,一辆车也可以有多次违章记录,车辆与违章记录关系也为1:m
create table user ( id int primary key, name varchar(20)); create table cars ( id int primary key, name varchar(20), user_id int, foreign key(user_id) references user(id)); create table vehicle_violation_information ( user_id int, cars_id int, foreign key(user_id) references user(id), foreign key(cars_id) references cars(id)); insert into cars values(123, 'crown', 1); insert into cars values(357, 'maybach', 2); insert into cars values(789, 'ford', 1); insert into vehicle_violation_information values (1, 123), (2, 357);

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

设计一个学校宿舍管理系统
学校宿舍管理系统,要求包含宿舍信息,学生信息,每日的宿舍查房记录。
一个食堂有多个仓口卖饭,关系为1:m,每个仓口卖饭可以有很多次,仓口与收费记录也是1:m
create table dormitory (id int primary key); create table student ( id int primary key, name varchar(20), dormitory_id int, foreign key(dormitory_id) references dormitory(id)); create table records ( id int primary key, dormitory_id int, status bit, record_time datetime, foreign key(dormitory_id) references dormitory(id)); insert into dormitory values (301), (302); insert into student values (1, 'zhangsan', 301), (2, 'lisi', 301), (3, 'wangwu', 301), (4, 'zhaoliu', 302); insert into records values (1, 301, 1, '2021-12-12 12:12:12'); insert into records values (2, 302, 0, '2021-12-12 13:13:13'); mysql> select * from dormitory; +-----+ | id| +-----+ | 301 | | 302 | +-----+ 2 rows in set (0.00 sec)mysql> select * from student; +----+----------+--------------+ | id | name| dormitory_id | +----+----------+--------------+ |1 | zhangsan |301 | |2 | lisi|301 | |3 | wangwu|301 | |4 | zhaoliu|302 | +----+----------+--------------+ 4 rows in set (0.00 sec)mysql> select * from records; +----+--------------+--------+---------------------+ | id | dormitory_id | status | record_time| +----+--------------+--------+---------------------+ |1 |301 || 2021-12-12 12:12:12 | |2 |302 || 2021-12-12 13:13:13 | +----+--------------+--------+---------------------+ 2 rows in set (0.00 sec)

设计一个考勤系统
考勤系统,包含员工表,考勤记录表
create table staff (id int primary key, name varchar(20)); create table attenance ( id int primary key, staff_id int, info_time datetime, foreign key(staff_id) references staff(id)); insert into staff values (1, 'zhangsan'), (2, 'lisi'); insert into attenance values (1, 001, '2021-03-03 9:00:00'); insert into attenance values (2, 002, '2021-03-03 9:00:00'); mysql> select * from staff; +----+----------+ | id | name| +----+----------+ |1 | zhangsan | |2 | lisi| +----+----------+ 2 rows in set (0.00 sec)mysql> select * from attenance; +----+----------+---------------------+ | id | staff_id | info_time| +----+----------+---------------------+ |1 |1 | 2021-03-03 09:00:00 | |2 |2 | 2021-03-03 09:00:00 | +----+----------+---------------------+ 2 rows in set (0.00 sec)

二、表的设计 所谓的 “数据库设计” “表的设计” 其实就是根据实际的问题场景,把表给创建出来了
【JavaSE|MySQL 数据库约束、聚合查询、多表查询】给你一个问题场景,如何设计数据库,如何设计表?
一个典型的通用的办法:先找出这个场景中涉及到的 “实体” 然后再来分析“实体之间的关系"
实体 => 对象
实体就可以视为是需求中的一些关键性的名词
一个典型的场景:学生管理系统
  1. 表示学生的基本信息
  2. 表示班级的基本信息.
  3. 表示学生学习的课程的基本信息
对于咱们找到的实体来说,就需要创建对应的表来表示相关的信息 (每个实体表,里面的基本信息,结合需求都是容易确定的)
很多时候,实体和实体之间,并不是孤立的,而是存在对应关系,这样的对应关系,也需要体现在表中 (实体之间的关系,这个是隐含的,是需要进一步分析才能想清楚的。这里实体之间的不同的关系,会对表的设计产生直接的影响)
实体之间的关系:
  1. 一对一的关系
  2. —对多的关系
  3. 多对多的关系
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

三、新增 INSERT INTO table_name [(column [, column ...])] SELECT ...
和查询结合在一起的新增操作,把从上一个表中的查询结果,作为下一个表要插入的数据
案例1:创建一张用户表,设计有name姓名、email邮箱、sex性别、mobile手机号字段。需要把已有的
学生数据复制进来,可以复制的字段为name、qq_mail
-- 创建用户表 DROP TABLE IF EXISTS test_user; CREATE TABLE test_user ( id INT primary key auto_increment, name VARCHAR(20) comment '姓名', age INT comment '年龄', email VARCHAR(20) comment '邮箱', sex varchar(1) comment '性别', mobile varchar(20) comment '手机号' ); -- 将学生表中的所有数据复制到用户表 insert into test_user(name, email) select name, qq_mail from student;

案例2:把A的记录给插入到B中:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 在这个语句中,就会先执行查找。针对查找到的每个结果,都执行插入操作,插入到B中,我们需要保证,从A中查询出来的结果的列数和类型,和B表匹配
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 接下来,把B的列的顺序调换一下,此时能否完成把A的数据给插入到B这个操作呢?只要保证A
    的查询结果的列的顺序和B对应即可
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 本来A表中,id在前,name在后,和B的列的顺序不匹配,但是可以通过针对A进行指定列查询,从而可以保证查询结果的顺序能和B对上
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

另外,还可以给后面的 select 指定一些其他的条件 / 排序 /limit / 去重,插入的实际就是select执行结果的临时表,得保证插入之后数据是合理的
四、查询 1、聚合查询 1.1、聚合函数
把多行之间的数据,给进行聚合
MySQL内置了一些聚合函数,可以让我们直接来使用
常见的统计总数、计算平局值等操作,可以使用聚合函数来实现,常见的聚合函数有:
函数 说明
COUNT([DISTINCT] expr) 返回查询结果有多少行
SUM([DISTINCT] expr) 返回查询到的数据的 总和,不是数字没有意义
AVG([DISTINCT] expr) 返回查询到的数据的 平均值,不是数字没有意义
MAX([DISTINCT] expr) 返回查询到的数据的 最大值,不是数字没有意义
MIN([DISTINCT] expr) 返回查询到的数据的 最小值,不是数字没有意义
案例:
-- 创建考试成绩表 DROP TABLE IF EXISTS exam_result; CREATE TABLE exam_result ( id INT, name VARCHAR(20), chinese DECIMAL(3,1), math DECIMAL(3,1), english DECIMAL(3,1) ); -- 插入测试数据 INSERT INTO exam_result (id,name, chinese, math, english) VALUES -> (1,'唐三藏', 67, 98, 56), -> (2,'孙悟空', 87.5, 78, 77), -> (3,'猪悟能', 88, 98.5, 90), -> (4,'曹孟德', 82, 84, 67), -> (5,'刘玄德', 55.5, 85, 45), -> (6,'孙权', 70, 73, 78.5), -> (7,'宋公明', 75, 65, 30);

count
-就相当于是针对 select * from exam_resul t的结果集合进行计算行数~
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • count这里的参数不一定非要写作*也可以指定某个列
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

sum
把这一列的若干行,进行相加
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 这个操作仍然是表达式查询
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • 聚合函数,也是表达式中的一个部分
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • sum这个操作只能针对数字进行运算,不能针对字符串来进行
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

where
聚合函数,还可以搭配where字句来使用.可以基于条件进行筛选,把筛选结果,在进行聚合
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

2、GROUP BY子句 2.1、group by
根据行的值,对数据进行分组.把值相同的行都归为一组
SELECT 中使用 GROUP BY 子句可以对指定列进行分组查询。需要满足:使用 GROUP BY 进行分组查询时,SELECT 指定的字段必须是“分组依据字段”,其他字段若想出现在SELECT 中则必须包含在聚合函数中
create table emp( id int primary key auto_increment, name varchar(20) not null, role varchar(20) not null, salary numeric(11,2) ); insert into emp(name, role, salary) values ('马云','服务员', 1000.20), ('马化腾','游戏陪玩', 2000.99), ('孙悟空','游戏角色', 999.11), ('猪无能','游戏角色', 333.5), ('沙和尚','游戏角色', 700.33), ('隔壁老王','董事长', 12000.66);

1、查询每个角色的最高工资、最低工资和平均工资
按照岗位,来进行分组
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

一个sql的执行过程,具体的执行顺序,和我们书写的顺序并不完全一致
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

计算avg 的时候, NULL这样的记录是不计入其中的,不会影响到平均值的结果
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

2.2、having
针对分组之后,得到的结果,可以通过having来进行指定条件
group by是可以使用where.只不过 where是在分组之前执行.如果要对分组之后的结果进行条件筛选,就需要使用having
分组前:
分组之前指定条件:还是求每种角色,平均薪资,要去掉马云,这里就是先去掉马云,然后再分组,分组之前指定的条件,就要使用 where
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

可以看到,正好就是马云是服务员,把马云去掉之后,分组结果中,就少了服务员这个记录
分组之后指定条件筛选:
求每种角色,平均薪资,只保留平均薪资1w以下,这里就是得先分组计算,知道了平均工资,才能进一步的筛选 (分组之后,指定的条件,就需要使用having 了)
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

很明显的看到,董事长这个平均薪资超过1w的记录已经没有了~~
3、联合查询 3.1、笛卡尔积
实际开发中往往数据来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积:
笛卡尔积 (多表查询中的核心操作), 笛卡尔积是针对任意两张表之间进行的运算
笛卡尔积的运算过程:
先拿第一张表的第一条记录,和第二张表的每个记录,分别组合,得到了一组新的记录,继续再拿第一张表的第二条记录
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

3.2、使用
如何在SQL中进行笛卡尔积:最简单的做法,就是直接 select * from表名,表名 之间使用逗号分割
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

笛卡尔积,是两张表中数据尽可能的排列组合得到的,
在这些排列组合中,仔细看,有没有一些数据是我们需要的,另外一些数据是没1意义的呢?
笛卡尔积是一个单纯无脑的排列组合,这里的组合结果不一定都是有意义的数据两张表中,都有classld这一列classld 的值对应相等的记录,其实就是应该要保留的记录,像这里的classld相等这样的条件,就称为 "连接条件"带有连接条件的笛卡尔积,其实就是 “多表查询了”
如果笛卡尔积中的两个列名字相同,在写条件的时候就可以通过表名.列名的方式来访问
如果列名不会混淆(不是同名的),可以用表名.列名,也可以省略表名
  • 修改后:
当在代码中加上筛选条件之后,很明显的看到,记录里就只剩下4条咱们需要的数据了
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

在最终的查询结果中,一般就只是需要一部分列来显示,就要哪个列就显式制定哪个列就行了
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

3.3、多表查询
drop table if exists classes; drop table if exists student; drop table if exists course; drop table if exists score; create table classes (id int primary key auto_increment, name varchar(20), `desc` varchar(100)); create table student (id int primary key auto_increment, sn varchar(20),name varchar(20), qq_mail varchar(20) , classes_id int); create table course(id int primary key auto_increment, name varchar(20)); create table score(score decimal(3, 1), student_id int, course_id int); insert into classes(name, `desc`) values ('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'), ('中文系2019级3班','学习了中国传统文学'), ('自动化2019级5班','学习了机械自动化'); insert into student(sn, name, qq_mail, classes_id) values ('09982','黑旋风李逵','xuanfeng@qq.com',1), ('00835','菩提老祖',null,1), ('00391','白素贞',null,1), ('00031','许仙','xuxian@qq.com',1), ('00054','不想毕业',null,1), ('51234','好好说话','say@qq.com',2), ('83223','tellme',null,2), ('09527','老外学中文','foreigner@qq.com',2); insert into course(name) values ('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文'); insert into score(score, student_id, course_id) values -- 黑旋风李逵 (70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6), -- 菩提老祖 (60, 2, 1),(59.5, 2, 5), -- 白素贞 (33, 3, 1),(68, 3, 3),(99, 3, 5), -- 许仙 (67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6), -- 不想毕业 (81, 5, 1),(37, 5, 5), -- 好好说话 (56, 6, 2),(43, 6, 4),(79, 6, 6), -- tellme (80, 7, 2),(92, 7, 6);

在这个场景中,涉及到到的实体,主要是三个
学生
班级
课程
学生和班级是—对多的关系
学生和课程是多对多的关系
分数表,其实就是学生和课程之间的关联表
  • desc:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

  • select:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

3.3.1、内连接 1)查询“许仙”同学的 成绩
select sco.score from student stu inner join score sco on stu.id=sco.student_id and stu.name='许仙'; -- 或者 select sco.score from student stu, score sco where stu.id=sco.student_id and stu.name='许仙';

许仙选了很多课,就需要在学生表中获取到学生姓名,在分数表中,获取到分数信息心,这里就需要针对学生表和分数表进行笛卡尔积
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

当前笛卡尔积,里面的东西太多了 160行,仔细观察就能发现,在当前的这两张表里,都存在学生id这个列,按照前面总结的规律,应该指定这两个id 匹配,才保留记录,不匹配的就属于是排列组合直接生成的无效数据
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

这个里面就体现出了,每个学生的每门课程,分数分别是多少
再加一个条件:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

此时就是只包含许仙的每门课的成绩了,只需要许仙的成绩,其他列就都不要了只保留名字和分数
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

实现刚才这个多表查询,直接from 多张表,是一种写法,除此之外,还有另外一种写法.基于join 这样的关键字,也能实现多表查询
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

2)查询所有同学的总成绩,及同学的个人信息:
-- 成绩表对学生表是多对1关系,查询总成绩是根据成绩表的同学id来进行分组的 SELECT stu.sn, stu.NAME, stu.qq_mail, sum( sco.score ) FROM student stu JOIN score sco ON stu.id = sco.student_id GROUP BY sco.student_id;

这个案例要在多表查询的基础上,再加上一个聚合查询
这个效果就是显示出每个同学的每个科目的分数,此处可以看到,同学的分数,是按照行的方式来排列的:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

加上 group by id 之后,可以看到,记录的行数明显变少了.每个同学只有一行数据了
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

分组之后,可以看到,当前这里的score列并不是总成绩,而是每个分组中的第一条记录,此处要想得到总成绩,就需要进行sum操作:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

3)查询所有同学的成绩,及同学的个人信息:
这里不光要查询出同学的名字,还有课程名字、分数
这个时候。就涉及到三张表的联合查询了
同学名字 => 学生表
课程名字 => 课程表
分数 => 分数表
三张表算笛卡尔积和两张表,规则都一样
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

当前这个表就列出了每个同学的每个课程拿到的分数.同时带有课程的名字,去掉不必要的列,只保留关注的关键列:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

如果是 join on 的写法,能否针对三张表进行联合:
from表1 join表2 on条件join表3 on条件
select student.name, course.name, score.score from student join score on student.id = score.student_id join course on score.course_id = course.id;

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

上面说的这个from 多个表 where 写法叫做 内连接
使用join on 的写法,既可以表示 内连接,还可以表示 外连接
3.3.2、外连接
select列from表1 inner join表2 on条件;

inner join表示是 “内连接” 其中inner可以省略
select列 from表1 left join表2 on条件; -- 左外连接 select列 from表1 right join表2 on条件; -- 右外连接

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

按照之前介绍过的笛卡尔积操作,很明显,这里的记录是少了一个4的同学的记录:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

上面的写法,就相当于是一个inner join
left join:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

通过刚才的例子,可以看到 left joininner join 之间的区别,
inner join:要求的是两个表里都同时有的数据,
left join : 以左侧的表为主,会尽可能的把左侧的表的记录都列出来,大不了后侧的表的对应列填成NULL
right join 也是类似:以右侧的表为主,尽可能把右侧的记录都列出来,大不了左侧的表对应的列填成NULL
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

内连接里的记录就只是包含两个表中同时拥有的记录:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

左外连接就是以左侧表为主,左侧表中的每个记录都在左外连接中有体现:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

右外连接就是以右侧表为主,右侧表的每个记录都在结果中有体现:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

是否存在一种连接,能够得到这种效果呢?这种称为 “全外连接” ,但是,mysql不支持
3.3.3、自连接 自连接是指在同一张表连接自身进行查询
什么时候需要使用自连接:自连接的本质其实是把行和行之间的比较条件,转换成列和列
示例:显示所有“计算机原理”成绩比“Java”成绩高的成绩信息
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

SQL指定条件,都是按照列和列之间进行指定的
而这个问题下的表,科目之间已经不是列了,而是行了
在这个问题中,可以看到,这里的条件是按照行的方式来排列的,为了解决这个问题,就需要把行,给转换成列把行转换成列,就可以用到 “自连接"
自连接同名,就指定两个名字:
select * from score as s1, score as s2;

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

共有四百条记录,
按照 student_id 进行连接条件之后产生的笛卡尔积:
共62条记录
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

本来这里的 98.5 和 70.5 是同一个列的两行,经过了笛卡尔积之后,已经分布到两个列中了,是当前这里的这两列,中间还是有着不少的无效数据的,为了能够更好的进行比较,再加上一些筛选条件,比如,就让s1的课程id只保留3的记录,就让s2的课程id只保留1的记录:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

最后加上判断大小:
select * from score as s1, score as s2 where s1.student_id = s2.student_id and s1.course_id = 3 and s2.course_id = 1 and s1.score > s2.score;

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

3.3.4、子查询 子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
1) 单行子查询:返回一行记录的子查询
查询与“不想毕业” 同学的同班同学:
先知道不想毕业这个同学的班级id,然后根据班级id在学生表中筛选同id的同学:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

合并:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

2)多行子查询:返回多行记录的子查询
案例:查询“语文”或“英文”课程的成绩信息
先查询语文和英文课程 id,再根据课程 id 找到对应的成绩信息:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

合并:
select * from score where course_id in (select id from course where name = '语文' or name = '英文');

JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

1.可以使用多列包含:
-- 插入重复的分数:score, student_id, course_id列重复 insert into score(score, student_id, course_id) values -- 黑旋风李逵 (70.5, 1, 1),(98.5, 1, 3), -- 菩提老祖 (60, 2, 1); -- 查询重复的分数 SELECT * FROM score WHERE ( score, student_id, course_id ) IN ( SELECT score, student_id, course_id FROM score GROUP BY score, student_id, course_id HAVING count( 0 ) > 1 );

2.[NOT] EXISTS关键字:
-- 使用 EXISTS select * from score sco where exists (select sco.id from course cou where (name='语文' or name='英文') and cou.id = sco.course_id); -- 使用 NOT EXISTS select * from score sco where not exists (select sco.id from course cou where (name!='语文' and name!='英文') and cou.id = sco.course_id);

在from子句中使用子查询:子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用。
查询所有比“中文系2019级3班”平均分高的成绩信息:
-- 获取“中文系2019级3班”的平均分,将其看作临时表 SELECT avg( sco.score ) score FROM score sco JOIN student stu ON sco.student_id = stu.id JOIN classes cls ON stu.classes_id = cls.id WHERE cls.NAME = '中文系2019级3班';

查询成绩表中,比以上临时表平均分高的成绩:
SELECT * FROM score sco, ( SELECT avg( sco.score ) score FROM score sco JOIN student stu ON sco.student_id = stu.id JOIN classes cls ON stu.classes_id = cls.id WHERE cls.NAME = '中文系2019级3班' ) tmp WHERE sco.score > tmp.score;

3.3.5、合并查询 在实际应用中,为了合并多个 select 的执行结果,可以使用集合操作符 unionunion all 。通过 union 把两个 sq l的查询结果给合并到一起了,合并的前提是两个sql查询的列得是对应的。
union :该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行
案例:查询id小于3,或者名字为“英文”的课程:
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

也可以使用or来替换,使用or的时候,必须保证你是针对同一个表来指定的多个条件查询,union 不一定是针对同一张表
JavaSE|MySQL 数据库约束、聚合查询、多表查询
文章图片

union all : 该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行

    推荐阅读