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

一、数据库约束

约束就是数据库在使用的时候,对于里面能够存的数据提出的要求和限制,程序猿就可以借助约束来完成更好的校验

1、约束类型

① NULL约束

NOT NULL - 指示某列不能存储 NULL 值,插入会直接报错

  • 给id这一列设为not null 之后,此时这里的值就变成NO了

在这里插入图片描述

  • 当尝试插入空值的时候,就直接会报错,插入失败

在这里插入图片描述

  • NOT NULL是可以给任意个列来进行设置,不仅仅是这一个列

在这里插入图片描述


② UNIQUE:唯一约束

UNIQUE - 保证某列的每行必须有唯一的值,尝试插入也会报错。

在这里插入图片描述
在这里插入图片描述


③ DEFAULT:默认值约束

DEFAULT - 规定没有给列赋值时的默认值

  • MySQL中,默认的默认值,是 NULL

在这里插入图片描述
在这里插入图片描述

  • 此处也可以把默认的默认值给改成其他咱们需要的内容

在这里插入图片描述


④ PRIMARY KEY:主键约束

PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
主键约束,相当于数据的唯一身份标识。类似于身份证号码 / 手机号码。
这个是咱们日常开发的时候最常使用的约束!最重要的约束。
创建表的时候,很多时候都需要指定主键。

  • 对于一个表来说,只能有一个列被指定为主键

在这里插入图片描述

  • 关于主键,典型的用法,就是直接使用1,2,3,4整数递增的方式来进行表示
    MySQL里面对于这种递增的主键,是有内置支持的,称为 "自增主键"

在这里插入图片描述

  • 当设定好自增主键之后,此时插入的记录,就可以不指定自增主键的值了(直接使用null 来表示),交给 mysql自行分配即可

在这里插入图片描述

  • 依靠mysql的自增主键来分配的
    每次新增一个新的记录,都会产生一个自增的id

在这里插入图片描述

  • 自增主键也是可以手动指定id的,一旦指定过之后,后续新插入的数据就都是从10之后来排了3-10之间的就用不了

在这里插入图片描述


上面说的这些约束,都是针对每个列单独设置的,只是针对这个列来说是这样的,不同的列之间没有影响


⑥ FOREIGN KEY:外键约束

FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
外键用于关联其他表的主键或唯一键

在这里插入图片描述
在这里插入图片描述

  • 构造一个学生表,来指定一下外键约束
    在这里插入图片描述

  • 在student表中插入数据:

在这里插入图片描述

  • 修改 student 表中的 Classid:

在这里插入图片描述


  • 刚才都是针对子表来操作,外键约束同样也在约束父表

在这里插入图片描述


外键约束的工作原理:

在子表中插入新的记录的时候,就会先根据对应的值,在父表中先查询,查询到之后,才能够执行后续的插入

这里的查询操作,可能是一个成本较高的操作(比较耗时),外键约束其实要求,父表中被依赖的这一列,必须要有索引,有了索引就能大大的提高查询速度~

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);

在这里插入图片描述

设计一个车辆违章系统
车辆违章系统,包含用户表,车辆表,违章信息表。违章信息表中包含用户和车辆的违章信息

用户可以拥有多辆车,关系为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);

在这里插入图片描述

设计一个学校宿舍管理系统
学校宿舍管理系统,要求包含宿舍信息,学生信息,每日的宿舍查房记录。

一个食堂有多个仓口卖饭,关系为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)

二、表的设计

所谓的 “数据库设计” “表的设计” 其实就是根据实际的问题场景,把表给创建出来了

给你一个问题场景,如何设计数据库,如何设计表?
一个典型的通用的办法:先找出这个场景中涉及到的 “实体” 然后再来分析“实体之间的关系"
实体 => 对象
实体就可以视为是需求中的一些关键性的名词

一个典型的场景:学生管理系统

  1. 表示学生的基本信息
  2. 表示班级的基本信息.
  3. 表示学生学习的课程的基本信息

对于咱们找到的实体来说,就需要创建对应的表来表示相关的信息 (每个实体表,里面的基本信息,结合需求都是容易确定的)
很多时候,实体和实体之间,并不是孤立的,而是存在对应关系,这样的对应关系,也需要体现在表中 (实体之间的关系,这个是隐含的,是需要进一步分析才能想清楚的。这里实体之间的不同的关系,会对表的设计产生直接的影响)

实体之间的关系

  1. 一对一的关系
  2. —对多的关系
  3. 多对多的关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


三、新增

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中:

在这里插入图片描述

  • 在这个语句中,就会先执行查找。针对查找到的每个结果,都执行插入操作,插入到B中,我们需要保证,从A中查询出来的结果的列数和类型,和B表匹配

在这里插入图片描述

在这里插入图片描述

  • 接下来,把B的列的顺序调换一下,此时能否完成把A的数据给插入到B这个操作呢?只要保证A
    的查询结果的列的顺序和B对应
    即可

在这里插入图片描述

  • 本来A表中,id在前,name在后,和B的列的顺序不匹配,但是可以通过针对A进行指定列查询,从而可以保证查询结果的顺序能和B对上

在这里插入图片描述

另外,还可以给后面的 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的结果集合进行计算行数~

在这里插入图片描述

  • count这里的参数不一定非要写作*也可以指定某个列

在这里插入图片描述

sum

把这一列的若干行,进行相加

在这里插入图片描述

  • 这个操作仍然是表达式查询

在这里插入图片描述

  • 聚合函数,也是表达式中的一个部分

在这里插入图片描述

  • sum这个操作只能针对数字进行运算,不能针对字符串来进行

在这里插入图片描述

where

聚合函数,还可以搭配where字句来使用.可以基于条件进行筛选,把筛选结果,在进行聚合

在这里插入图片描述


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、查询每个角色的最高工资、最低工资和平均工资

按照岗位,来进行分组

在这里插入图片描述

一个sql的执行过程,具体的执行顺序,和我们书写的顺序并不完全一致

在这里插入图片描述

计算avg 的时候, NULL这样的记录是不计入其中的,不会影响到平均值的结果

在这里插入图片描述


2.2、having

针对分组之后,得到的结果,可以通过having来进行指定条件

group by是可以使用where.只不过 where是在分组之前执行.如果要对分组之后的结果进行条件筛选,就需要使用having

分组前:
分组之前指定条件:还是求每种角色,平均薪资,要去掉马云,这里就是先去掉马云,然后再分组,分组之前指定的条件,就要使用 where

在这里插入图片描述

可以看到,正好就是马云是服务员,把马云去掉之后,分组结果中,就少了服务员这个记录

分组之后指定条件筛选:
求每种角色,平均薪资,只保留平均薪资1w以下,这里就是得先分组计算,知道了平均工资,才能进一步的筛选 (分组之后,指定的条件,就需要使用having 了)

在这里插入图片描述
很明显的看到,董事长这个平均薪资超过1w的记录已经没有了~~


3、联合查询

3.1、笛卡尔积

实际开发中往往数据来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积

笛卡尔积 (多表查询中的核心操作), 笛卡尔积是针对任意两张表之间进行的运算

笛卡尔积的运算过程:
先拿第一张表的第一条记录,和第二张表的每个记录,分别组合,得到了一组新的记录,继续再拿第一张表的第二条记录

在这里插入图片描述


3.2、使用

如何在SQL中进行笛卡尔积:最简单的做法,就是直接 select * from表名,表名 之间使用逗号分割

在这里插入图片描述

笛卡尔积,是两张表中数据尽可能的排列组合得到的,
在这些排列组合中,仔细看,有没有一些数据是我们需要的,另外一些数据是没1意义的呢?
笛卡尔积是一个单纯无脑的排列组合,这里的组合结果不一定都是有意义的数据两张表中,都有classld这一列classld 的值对应相等的记录,其实就是应该要保留的记录,像这里的classld相等这样的条件,就称为 "连接条件"带有连接条件的笛卡尔积,其实就是 “多表查询了”

如果笛卡尔积中的两个列名字相同,在写条件的时候就可以通过表名.列名的方式来访问
如果列名不会混淆(不是同名的),可以用表名.列名,也可以省略表名

  • 修改后:

当在代码中加上筛选条件之后,很明显的看到,记录里就只剩下4条咱们需要的数据了

在这里插入图片描述
在最终的查询结果中,一般就只是需要一部分列来显示,就要哪个列就显式制定哪个列就行了

在这里插入图片描述


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','黑旋风李逵','[email protected]',1),
('00835','菩提老祖',null,1),
('00391','白素贞',null,1),
('00031','许仙','[email protected]',1),
('00054','不想毕业',null,1),
('51234','好好说话','[email protected]',2),
('83223','tellme',null,2),
('09527','老外学中文','[email protected]',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:

在这里插入图片描述

  • select:

在这里插入图片描述


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='许仙';

许仙选了很多课,就需要在学生表中获取到学生姓名,在分数表中,获取到分数信息心,这里就需要针对学生表和分数表进行笛卡尔积

在这里插入图片描述

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

在这里插入图片描述

这个里面就体现出了,每个学生的每门课程,分数分别是多少
再加一个条件:

在这里插入图片描述
此时就是只包含许仙的每门课的成绩了,只需要许仙的成绩,其他列就都不要了只保留名字和分数

在这里插入图片描述

实现刚才这个多表查询,直接from 多张表,是一种写法,除此之外,还有另外一种写法.基于join 这样的关键字,也能实现多表查询

在这里插入图片描述


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;

这个案例要在多表查询的基础上,再加上一个聚合查询

这个效果就是显示出每个同学的每个科目的分数,此处可以看到,同学的分数,是按照行的方式来排列的:

在这里插入图片描述
加上 group by id 之后,可以看到,记录的行数明显变少了.每个同学只有一行数据了

在这里插入图片描述
分组之后,可以看到,当前这里的score列并不是总成绩,而是每个分组中的第一条记录,此处要想得到总成绩,就需要进行sum操作:

在这里插入图片描述


3)查询所有同学的成绩,及同学的个人信息:

这里不光要查询出同学的名字,还有课程名字、分数
这个时候。就涉及到三张表的联合查询了
同学名字 => 学生表
课程名字 => 课程表
分数 => 分数表
三张表算笛卡尔积和两张表,规则都一样

在这里插入图片描述

当前这个表就列出了每个同学的每个课程拿到的分数.同时带有课程的名字,去掉不必要的列,只保留关注的关键列:

在这里插入图片描述
如果是 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;

在这里插入图片描述

上面说的这个from 多个表 where 写法叫做 内连接
使用join on 的写法,既可以表示 内连接,还可以表示 外连接


3.3.2、外连接

selectfrom1 inner join2 on条件;

inner join表示是 “内连接” 其中inner可以省略

selectfrom1 left join2 on条件; -- 左外连接
selectfrom1 right join2 on条件; -- 右外连接

在这里插入图片描述
在这里插入图片描述

按照之前介绍过的笛卡尔积操作,很明显,这里的记录是少了一个4的同学的记录:

在这里插入图片描述
上面的写法,就相当于是一个inner join

left join:

在这里插入图片描述
通过刚才的例子,可以看到 left joininner join 之间的区别,

inner join要求的是两个表里都同时有的数据

left join以左侧的表为主,会尽可能的把左侧的表的记录都列出来,大不了后侧的表的对应列填成NULL
right join 也是类似:以右侧的表为主,尽可能把右侧的记录都列出来,大不了左侧的表对应的列填成NULL

在这里插入图片描述
内连接里的记录就只是包含两个表中同时拥有的记录:

在这里插入图片描述
左外连接就是以左侧表为主,左侧表中的每个记录都在左外连接中有体现

在这里插入图片描述
右外连接就是以右侧表为主,右侧表的每个记录都在结果中有体现

在这里插入图片描述

是否存在一种连接,能够得到这种效果呢?这种称为 “全外连接” ,但是,mysql不支持


3.3.3、自连接

自连接是指在同一张表连接自身进行查询

什么时候需要使用自连接:自连接的本质其实是把行和行之间的比较条件,转换成列和列

示例:显示所有“计算机原理”成绩比“Java”成绩高的成绩信息

在这里插入图片描述

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

select * from score as s1, score as s2;

在这里插入图片描述

共有四百条记录,

按照 student_id 进行连接条件之后产生的笛卡尔积:
共62条记录

在这里插入图片描述
本来这里的 98.5 和 70.5 是同一个列的两行,经过了笛卡尔积之后,已经分布到两个列中了,是当前这里的这两列,中间还是有着不少的无效数据的,为了能够更好的进行比较,再加上一些筛选条件,比如,就让s1的课程id只保留3的记录,就让s2的课程id只保留1的记录:

在这里插入图片描述

最后加上判断大小:

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;

在这里插入图片描述


3.3.4、子查询

子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询

1) 单行子查询:返回一行记录的子查询

查询与“不想毕业” 同学的同班同学:

先知道不想毕业这个同学的班级id,然后根据班级id在学生表中筛选同id的同学:

在这里插入图片描述
合并:

在这里插入图片描述


2)多行子查询:返回多行记录的子查询
案例:查询“语文”或“英文”课程的成绩信息

先查询语文和英文课程 id,再根据课程 id 找到对应的成绩信息:

在这里插入图片描述
合并:

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

在这里插入图片描述


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,或者名字为“英文”的课程:

在这里插入图片描述
也可以使用or来替换,使用or的时候,必须保证你是针对同一个表来指定的多个条件查询,union 不一定是针对同一张表

在这里插入图片描述

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

猜你喜欢

转载自blog.csdn.net/qq_56884023/article/details/123406017