一、数据库约束
约束就是数据库在使用的时候,对于里面能够存的数据提出的要求和限制,程序猿就可以借助约束来完成更好的校验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)
二、表的设计 所谓的 “数据库设计” “表的设计” 其实就是根据实际的问题场景,把表给创建出来了
【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中:
文章图片
- 在这个语句中,就会先执行查找。针对查找到的每个结果,都执行插入操作,插入到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相等这样的条件,就称为 "连接条件"带有连接条件的笛卡尔积,其实就是 “多表查询了”
如果笛卡尔积中的两个列名字相同,在写条件的时候就可以通过表名.列名的方式来访问
如果列名不会混淆(不是同名的),可以用表名.列名,也可以省略表名
- 修改后:
文章图片
在最终的查询结果中,一般就只是需要一部分列来显示,就要哪个列就显式制定哪个列就行了
文章图片
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:
文章图片
- 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、外连接
select列from表1 inner join表2 on条件;
inner join表示是 “内连接” 其中inner可以省略
select列 from表1 left join表2 on条件;
-- 左外连接
select列 from表1 right join表2 on条件;
-- 右外连接
文章图片
文章图片
按照之前介绍过的笛卡尔积操作,很明显,这里的记录是少了一个4的同学的记录:
文章图片
上面的写法,就相当于是一个inner join
left join:
文章图片
通过刚才的例子,可以看到
left join
和 inner join
之间的区别,inner join
:要求的是两个表里都同时有的数据,left join
: 以左侧的表为主,会尽可能的把左侧的表的记录都列出来,大不了后侧的表的对应列填成NULLright 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 的执行结果,可以使用集合操作符
union
,union all
。通过 union 把两个 sq l的查询结果给合并到一起了,合并的前提是两个sql查询的列得是对应的。union
:该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行案例:查询id小于3,或者名字为“英文”的课程:
文章图片
也可以使用or来替换,使用or的时候,必须保证你是针对同一个表来指定的多个条件查询,union 不一定是针对同一张表
文章图片
union all
: 该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行推荐阅读
- 植物大战数据结构|植物大战 队列 —— 纯C
- JUnit5教程(1)(JUnit5架构和第一个测试示例)
- Java进阶|Java进阶学习——数据结构基础(二)
- 数据结构|小肥柴慢慢手写数据结构(C篇)(3-2 Stack应用举例)
- 数据结构|数据结构与算法入门前必读
- 数据结构|索引的数据结构
- 数据结构|数据结构与算法一篇帮助你吃下KMP算法
- 【数据结构·水滴计划】|【数据结构】一篇文章带你彻底吃透·算法复杂度
- java|2018-2019-20172329 《Java软件结构与数据结构》第二周学习总结