目录
[group by子句](#group by子句)
在MySQL数据库中,表的操作是最重要的,表的操作分为对表结构的操作和对表数据的操作,前几期我们已经学习了对表结构的操作,本期我们将学习对表数据的操作。
表的增删查改
对表数据的操作无非就四种操作,增(create) ,删除(delete) ,查(retrieve) ,改(update)。
增(create)
使用**insert [into]**关键字进行数据的增加操作,我们一般称为数据的插入操作。
创建一张学生表。
单行数据,全列插入
多行数据,指定列插入

在前几期我们已经学习了主键和唯一键,对于主键和唯一键而言,这一字段的数据是唯一的不能重复,所以如果我们此时插入了重复的数据,就会报错。

为了防止报错的情况,引入了更新和替换的方案。
1.判断插入的数据与表原有的数据产生主键或者唯一键冲突,如果冲突就更新冲突的数据,如果不冲突就直接插入。
通过 duplicate key 关键字实现。

2.判断插入的数据是否与表原有的数据产生主键和唯一键冲突,如果冲突就删除原有的数据,然后插入新的数据,如果没有冲突,则直接插入。
通过 replace into 关键字实现。

两种方法的区别就是 duplicate key 同步时,如果插入的数据和原数据冲突,就更新原数据而并不删除,而 replace into 关键字替换时,如果与原数据冲突,就会先删除原数据,然后再进行插入,所以区别就在于对冲突数据的处理方法上。
通常我们说表中的一个数据就是一个记录,也就是表的一行内容。
查(retrieve)
通过select关键字进行查询。
创建成绩表exam_result,并且插入对应的数据。

全列查询
查询 exam_result 的所有字段的数据。

指定列查询
查询 exam_result name字段,chinese字段的所有数据。

表达式字段查询
将查询出来的 chinese字段的值+10。

查询 name ,chinese+math+english 字段,可以获得总成绩。

给字段起别名
查询 name ,chinese+math+english 字段,给chinese+math+english 起名为总分,可以获得总成绩。

查询 总分<200 分的学生的成绩。

此时我们惊奇的发现,mysql提示我们 总分 这个字段无法识别,这是为什么呢?
基于此,我们就要引入了sql语句执行顺序的概念,我们规定在sql语句的执行中,where子句是先于select子句执行的,所以就自然不能使用select中重命名之后的字段。
可以理解为是where子句先筛选数据,筛选完数据之后,此时select子句再去进行查询。
所以要实现上述情景,就不能使用重命名之后的字段,必须使用数据库表原有的字段。图示如下。

查询结果去重
使用distinct关键字可以对查询出来的数据进行去重。
通过exam_result 表发现math 字段有重复的数据,所以我们对查询出来的math字段的数据进行去重。

以上我们查询出来的数据都是没有进行筛选过的数据,而数据库表中所有的数据,如果我们要对查询出来的数据进行约束,就要使用where条件。
where条件
比较运算符
|-------------------|----------------------------------------------------------|
| 运算符 | 说明 |
| >, >=, <, <= | 大于,大于等于,小于,小于等于 |
| = | 等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL |
| <=> | 等于,NULL 安全,例如 NULL NULL 的结果是 TRUE(1) |
| !=, <> | 不等于 |
| BETWEEN a0 AND a1 | 范围匹配,value属于[a0, a1],如果 a0 <= value <= a1,返回 TRUE(1) |
| IN (option, ...) | (option,...)括号中的可以理解为是一个集合 |
| IS NULL | 是 NULL |
| IS NOT NULL | 不是 NULL |
| LIKE模糊匹配 | % 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符 |
逻辑运算符
|---------|-------------------------------|
| 运算符 | 说明 |
| AND | 多个条件必须都为 TRUE(1),结果才是 TRUE(1) |
| OR | 任意一个条件为 TRUE(1), 结果为 TRUE(1) |
| NOT | 条件为 TRUE(1),结果为 FALSE(0) |
查询示例
查询英文成绩不及格(<60)的同学以及英语成绩。

查询语文成绩在[80,90]分的同学及语文成绩。

查询数学成绩是58或者59或者98或者99分的同学及数学成绩。
有两种方法,一种是使用or进行连接,一种是使用in条件。

查询姓孙的同学 和孙某同学。
通过like进行模糊查询,% 代表任意字符,**_**代表一个字符。

查询语文成绩好于英语成绩的同学及他们的英语成绩和语文成绩。

查询总分在200分以下的同学。

查询语文成绩 > 80 并且不姓孙的同学。

查询孙某 同学,否则要求 总成绩>200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80。

查询 qq号为空 的学生,和 qq号不为空的学生。

我们发现null是不能直接使用**=** 进行判断的,必须使用 <=> 进行判断 ,一般情况下,判空的操作我们一般使用 is null 或者 is not null。
null 和 **' '**的区别?

简单的来说 ' ' 代表有数据,数据是一个空串,而null 代表没有数据。比如说学校的考试,小明去了教室考了零分,和小明缺考是不一样的,考了零分证明考了试,只不过成绩为0,缺考就证明没有考试。
查询结果排序
通常我们使用 order by 子句对于查询出来的数据进行排序,asc 排升序,desc排降序。
查询同学的数学成绩 ,并对数学成绩升序排序。

查询同学qq号 ,并对qq号进行升序排序。

注意:null比任何值都小,所以排在最上面。
查询同学各门成绩 ,依次按数学降序 ,英语升序 ,语文升序的方式显示。

我们发现此时查询出来的结果只有数学成绩符合查询出的要求,这是因为一张表中的数据,不可能同时满足上述的数学成绩降序,英语成绩升序,语文成绩升序。既然无法同时满足,那就满足第一个查询的要求,即数学成绩降序排序。
查询同学总分 ,并且由高到低显示。

此时,我们竟然发现,order by 子句中竟然又可以使用别名了,这是为什么呢?
这是因为,sql语句的执行顺序是不一样的,一般情况下,先是通过where子句筛选出符合条件的数据,然后对筛选出的数据进行select查询,然后对查询出来的数据进行order by排序,最终显示。所以order by子句是后于select子句执行的,所以当然可以使用select子句中设置的字段别名。
也正是因为where 和 order by子句的执行顺序是不一样的所以,这两个子句是可以同时出现的。
查询姓孙 的同学或者 姓曹 的同学 数学成绩 ,结果按数学成绩由高到低显示 。

筛选分页结果
有时候我们可能会有只显示查询的数据的前几行的需求,所以此时就引入了筛选分页结果的概念。
通过limit关键字进行实现。
limit n :显示前n行。
limit s ,n:从s开始,显示n行。
limit n offset s:从s开始,显示n行。


改(update)
通常我们使用update ... set关键字进行数据的修改。
将孙悟空 同学的数学成绩变为80分。

将曹孟德 同学的数学成绩 更改为60 分,语文成绩 更改为70 分。

将总成绩倒数前三 的3位同学 的数学成绩加上30分。

要注意的是,update子句 的执行顺序一定是在order by子句 之后才执行的,可以理解为先通过order by 子句将chinese+english+math 字段查询了出来并且进行了排序然后最终保留了倒数前3的3个记录 ,最终由update子句进行数据更改。
将所有同学的语文成绩更改为原来的两倍。

如果没有where子句作为筛选条件,则最终更改的就是表中这一字段的所有数据,一般情况下我们不建议全表数据的更改,应该写上对应的where子句作为筛选的条件。
删(delete)
删除数据
使用 delete from关键字进行数据的删除操作。
删除孙悟空同学的成绩。

删除整张表的数据。
新创建一个用于删除数据的表。

插入相应的数据。

删除整张表的数据。

往空表中再次插入一条数据。

我们发现,其实我们删除了整张表的数据之后,再次往空表中插入数据时,此时的主键依然是从4开始的,意味着自增并没有清零。
查看表结构,发现此时的主键id对应的auto_increment 字段显示为5,意味着下一条数据的主键从5开始自增。

截断表
简单来说,截断其实就是删除,通过truncate 关键字对表进行截断 的操作,需要注意的是,truncate只能对整张表的数据进行截断,也就是说只能删除整张表的数据,而不能删除一条数据。
创建要截断的表。

向表中插入数据。

删除所有数据,截断整张表。

向空表中插入一条数据。

我们发现此时插入的数据的自增长的主键值竟然变成了1。
查看表结构。

我们发现此时的自增长主键的值变成了2,意味着下一条数据的主键在没有设置的条件下会默认为2,也就意味着在使用truncate进行截断表时,自增的主键的自增值也会被清零。
综上,delete from 删除整张表和truncate 删除整张表的区别就是,delete from 不会清空表的自增主键的自增值,后续插入数据,数据的主键依然会按照上一条数据的主键的值进行自增;而 truncate 删除整张表时会重置表的自增主键的自增值,后续再进行数据插入时,自增主键的值又会从起始值1开始自增。
查询的结果作为数据插入其它表
select查询出来的数据,也可以作为源数据插入其它的表。
使用 insert into ... select... 实现。
要实现对表数据的去重操作,创建duplicate_table表,并插入数据。

查询duplicate_table的所有数据并对查询的数据进行去重,将去重之后的数据作为源数据插入新表,使用select distinct只是对查询的结果进行了去重,对表本身所存储的数据是没有去重的。

创建 no_duplicate_table为空表,可以使用 cteate table A like B;的sql语句创建A表,这样创建出来的表是一个与B表结构相同的空表。

将在 duplicate_table 中查询的不重复的数据作为源数据插入 no_duplicate_table 之中。

通过表的重命名实现表的数据的去重操作。no_duplicate_table表中是去重的数据,而duplicate_table中数据是没有去重的,所以先将duplicate_table重命名为old_duplicate_table,然后再将no_duplicate_table重命名为duplicate_table,此时就实现了原来的duplicate_table表中数据的去重。

聚合函数
|--------------------------|------------------------|
| 函数 | 说明 |
| COUNT([DISTINCT] expr) | 返回查询到的数据的 数量 |
| SUM([DISTINCT] expr) | 返回查询到的数据的 总和,不是数字没有意义 |
| AVG([DISTINCT] expr) | 返回查询到的数据的 平均值,不是数字没有意义 |
| MAX([DISTINCT] expr) | 返回查询到的数据的 最大值,不是数字没有意义 |
| MIN([DISTINCT] expr) | 返回查询到的数据的 最小值,不是数字没有意义 |
查询统计操作
统计班级共有多少同学。

统计班级qq号有多少。

需要注意的是,null本身不计入个数的多少。
统计本次考试的数学成绩分数个数。

统计本次考试中不同的数学成绩的个数,简单来说就是对数学成绩去重之后数学成绩的个数。

统计数学成绩总分。

统计三门成绩的平均总分。

统计英语成绩最高分。

统计数学成绩>70分的最低数学成绩。

上述情景,得先通过where子句筛选出>70分的数学成绩,然后再通过select子句查询 筛选出来的>70分的所有数学成绩中最低的数学成绩。
group by子句
导入三个表,emp员工表,dept部门表,salgrade工资等级表。

显示每个部门的最高工资和平均工资。
做一个简单的分析,我们发现部门的编号和部门工资,这两个字段在emp表内,所以我们要先对emp表中的员工记录按照部门编号进行分组,一组代表一个部门,然后再对每组数据进行查询,就可以得到查询结果。

显示每个部门每种岗位的平均工资和最低工资。
做一个简单的分析,部门编号和岗位名称都在emp表里,所以首先对emp表进行查询,然后按照部门编号和岗位名称进行分组,一组就是一个部门的一个岗位所有员工的信息,然后依次对每组进行查询,就可以得到查询结果。

显示平均工资低于2000的部门和它的平均工资。

上述查询我们先使用group by对deptno进行了分组,每组就是一个部门的所有员工信息,然后select查询出了分组之后 每个部门的部门编号和平均工资,最终使用having子句对查询出的每组的结果进行筛选最终显示了最终的查询结果。
需要注意的是我们发现having子句中是可以用select中起的别名的,所以就可以得知having子句一定是后于select子句执行。
因为having子句后于select执行,而where子句先于select子句执行,所以就意味着where和having是可以同时存在的。
显示部门编号 >10 且平均工资大于 1000 的所有部门的部门编号和部门平均工资。

简单来说,where 和 having 都可以作为限制条件,不过where子句先于select执行,而having子句在select之后执行。
以上便是本期的基本查询相关的所有内容。
本期内容到此结束^_^