目录
简介
继上两篇博客我们继续来说关于MySQL的基础语句,
一、子查询
1.having过滤
使用 HAVING
实现聚合后过滤
select id,sum(score) sum_score
from scores
group by id
having sum_score>=500;
select id,sum(score) sum_score
:选取id
字段,并对score
字段求和,将求和结果别名设为sum_score
,明确要查询的内容 。from scores
:从名为scores
的表中获取数据,确定数据源 。group by id
:按照id
字段进行分组,相同id
的记录会被归为一组,后续聚合计算(如sum(score)
)是基于分组进行的 。having sum_score>=500
:HAVING
子句用于对分组后的结果进行过滤,这里筛选出sum_score
(即分组内score
求和结果 )大于等于 500 的分组,只返回满足该条件的分组数据 。HAVING
是在分组之后执行,能使用聚合函数的计算结果做条件,弥补WHERE
不能处理聚合结果过滤的问题 。
使用子查询实现类似逻辑(另一种思路,先聚合再过滤)
-- 使用子查询
-- 先执行子查询,将子查询的结果当成一张新的表再执行外面的查询
select * from
(
select id,sum(score) sum_score
from scores
group by id
) as a
where sum_score >= 500;
子查询部分(括号内):
select id,sum(score) sum_score
from scores
group by id
- 先从
scores
表按id
分组,计算每个分组score
总和并命名为sum_score
,这一步得到的是一个包含id
和对应分组score
总和的中间结果集,相当于一张临时表 。 as a
:给子查询的结果集起别名a
,方便外部查询引用 。- 外部查询
select * from ... where sum_score >= 500
:把子查询结果当作名为a
的 “新表”,从这张 “表” 里选取所有字段(*
表示所有字段 ),并通过WHERE
子句过滤出sum_score
大于等于 500 的记录 。这种方式是先通过子查询完成分组聚合得到结果集,再对结果集用WHERE
做常规行过滤,和HAVING
实现的最终效果类似,但执行逻辑、语法结构有区别,实际使用中二者在功能和性能场景上有不同适用情况(比如复杂子查询嵌套等场景下写法灵活度有差异 )。
2.limit限制查询
select * from students limit 10;
-- 从10开始,取20条
select * from students limit 10,20;
-
select * from students limit 10;
select *
:表示选取students
表中的所有字段(*
是通配符,代表所有列 )。from students
:指定数据来源是名为students
的表 。limit 10
:LIMIT
子句用于限制查询结果返回的记录数量,这里表示只从查询结果里选取前 10 条记录返回 ,常用于分页查询、获取固定数量样本数据等场景,比如只想看学生表中最前面 10 条学生信息时就可以这么用。
-
select * from students limit 10,20;
- 同样,
select * from students
是选取students
表所有字段数据 。 limit 10,20
:LIMIT
子句里第一个参数10
表示 “偏移量”,即从结果集的第10
条记录之后开始(数据库中记录行号一般从0
开始计数,所以这里实际是从第11
条物理记录开始 );第二个参数20
表示要选取的记录数量,也就是从偏移位置开始,往后取20
条记录 。常用来实现分页功能,比如页面要展示第 2 页(假设每页 20 条 )数据时,就可以用limit 10,20
(第 1 页是limit 0,20
,第 3 页是limit 20,20
,以此类推 ),获取对应页的学生记录 。
- 同样,
3.if条件判断
select id,name,sex,if(sex='男',1,2) as new_sex from students;
select id,name,sex,...
:选取students
表中的id
、name
、sex
字段数据,用于展示基础信息 。if(sex='男',1,2) as new_sex
:IF
是 SQL 里的条件判断函数,语法为IF(条件, 条件为真返回值, 条件为假返回值)
。这里判断sex
字段的值是否为'男'
:- 若为
'男'
,则返回1
; - 若不是
'男'
(通常就是'女'
等其他值 ),则返回2
。
最后用as new_sex
给这个条件判断结果起别名new_sex
,作为新的字段展示,整体就是查询学生基础信息同时,按性别做简单映射转换输出新的标识 。
- 若为
CASE WHEN
语句实现条件判断
select id,name,sex,
case when sex='男' then 1
when sex='女' then 2
else 3 end as new_sex
from students;
select id,name,sex,...
:同样先选取students
表的id
、name
、sex
字段 。case when sex='男' then 1
:CASE WHEN
是更灵活的条件分支结构,这里第一个分支判断sex
为'男'
时,返回1
;when sex='女' then 2
:第二个分支,判断sex
为'女'
时,返回2
;else 3 end
:前面分支都不满足时(比如sex
是其他值 ),执行else
分支返回3
,最后用as new_sex
给这一系列条件判断结果起别名new_sex
。
相比IF
函数,CASE WHEN
支持多条件分支(不止简单的二元判断 ),复杂场景下更易用,这里也是实现根据性别映射输出新标识的需求 。
-- 给总分评级
select
id,
sum_score,
case
when sum_score>=0 and sum_score<100 then '*'
when sum_score>=100 and sum_score<200 then '**'
when sum_score>=200 and sum_score<300 then '***'
when sum_score>=300 and sum_score<400 then '****'
when sum_score>=400 and sum_score<500 then '*****'
when sum_score>=500 and sum_score<600 then '******'
else '*******' end as stat
from
(
select
id,
sum(score) as sum_score
from
scores
group by
id
) as a;
- 子查询部分:
- 先执行
(select id, sum(score) as sum_score from scores group by id) as a
,从scores
表按id
分组,计算每个id
对应的score
总和,别名sum_score
,结果集命名为a
。
- 先执行
- 主查询部分:
select id, sum_score, ...
选取子查询结果里的id
、sum_score
字段,并用case when
语句根据sum_score
数值区间(0 - 100
、100 - 200
等 ),返回对应星号评级(*
到*******
),评级结果别名stat
,最终输出id
、总分、评级信息 。- 作用是对学生(或其他主体,由
id
关联 )的总分做区间评级,方便直观查看不同id
对应的总分等级 。
-- 给总分评级,统计每种评级的人数
select
stat,
count(1) as num
from
(
select
id,
sum_score,
case
when sum_score>=0 and sum_score<100 then '*'
when sum_score>=100 and sum_score<200 then '**'
when sum_score>=200 and sum_score<300 then '***'
when sum_score>=300 and sum_score<400 then '****'
when sum_score>=400 and sum_score<500 then '*****'
when sum_score>=500 and sum_score<600 then '******'
else '*******' end as stat
from
(
select
id,
sum(score) as sum_score
from
scores
group by
id
) as a
) as b
group by
stat;
4.grop by排序
-- 按总分排序
-- asc 升序, desc: 降序
select
id,
sum(score) as sum_score
from
scores
group by
id
order by sum_score desc;
- 功能:从
scores
表中,按id
分组计算每个id
的score
总和(别名sum_score
),再按照sum_score
字段 降序(desc
) 排列分组结果 。 - 关键语法:
GROUP BY id
:按id
分组,让sum(score)
针对每个id
单独计算总和 。ORDER BY sum_score desc
:基于分组计算出的sum_score
字段排序,desc
表示降序(若用asc
则是升序,默认不写ORDER BY
子句时结果无序 )。
-- 总分前十的学生
select
id,
sum(score) as sum_score
from
scores
group by
id
order by sum_score desc
limit 10;
除此之外的子查询还有上篇博客说的select form等
5.explain
查询执行计划
-- 打印sql的执行计划
explain select
id,
sum(score) as sum_score
from
scores
group by
id
order by
sum_score desc
limit 10;
explain
关键字:MySQL 中,在查询语句前加explain
,可让数据库返回这条查询的 执行计划 ,包含查询会用到的索引、表扫描方式、操作执行顺序等信息,用于分析查询性能瓶颈(比如是否全表扫描、索引是否有效利用 )。- 主查询逻辑:从
scores
表按id
分组,计算score
总和(sum_score
),再按sum_score
降序排序,最后用limit 10
取前 10 条结果 。explain
会拆解这个过程,展示数据库执行该查询的底层步骤 。
二、表关联
1.union合并查询结果
合并多张表 / 同表多次查询结果,列名以首次查询为准
-- 1. union
-- 两个sql结构要一致
select * from students where name='寇凝丹'
union all
select * from students where name='寇凝丹';
-- union: 去除重复数据
select * from students where name='寇凝丹'
union
select * from students where name='寇凝丹';
UNION ALL
部分:
两次执行select * from students where name='寇凝丹'
,用UNION ALL
合并结果。UNION ALL
会 保留所有结果行(包括重复行 ),如果students
表中name='寇凝丹'
的记录有 1 条,合并后结果是 2 条相同记录 。UNION
部分:
同样两次查询name='寇凝丹'
的记录,用UNION
合并。UNION
会 自动去除重复行(完全相同的记录只保留 1 条 ),若原表中该姓名记录是 1 条,合并后结果还是 1 条 。
2.笛卡尔积
-- 10、笛卡尔积
create table names(
id varchar(10),
name varchar(10)
);
insert into names values
('001','张三'),
('002','李四'),
('003','王五'),
('004','赵六');
create table ages(
id varchar(10),
age int
);
insert into ages values
('001',23),
('002',24),
('003',24),
('005',25);
-- 笛卡尔积
select * from names,ages;
- 建表与插入数据:
- 创建
names
表,含id
(字符串类型 )、name
(字符串类型 )字段,插入 4 条记录(张三、李四、王五、赵六 )。 - 创建
ages
表,含id
(字符串类型 )、age
(整数类型 )字段,插入 4 条记录(不同id
对应的年龄 )。
- 创建
- 笛卡尔积查询:
select * from names,ages;
执行时,数据库会将names
表的 每一条记录 与ages
表的 每一条记录 进行组合,结果集数量是两表记录数的乘积(这里names
4 条 ×ages
4 条 = 16 条结果 )。
笛卡尔积不考虑关联条件,会产生大量冗余组合,实际业务中常需用WHERE
加关联条件(如names.id = ages.id
)筛选有效关联,避免无意义数据 。
-- 再笛卡尔积的基础上增加条件实现表关联
select * from names,ages
where names.id=ages.id;
- 功能:先对
names
表和ages
表做笛卡尔积(两表所有记录组合 ),再通过where names.id=ages.id
筛选出id
字段匹配 的记录,最终结果等价于INNER JOIN
关联查询(只保留两表id
相同的组合 )。 - 作用:避免笛卡尔积的冗余数据,只保留表间
id
关联的有效记录,实现类似select * from names inner join ages on names.id=ages.id;
的效果(旧版 SQL 常用这种语法模拟连接,现代更推荐显式JOIN
写法 )。
3. JOIN
连接
3.1左连接(left join)
以左表数据为准,关联右表匹配行;右表无匹配时,用 NULL
填充右表字段
-- 2、left join(左关联)
-- 以左表为基础进行关联
select * from
names as a
left join
ages as b
on a.id=b.id;
- 功能:用
LEFT JOIN
关联names
表(别名a
,左表 )和ages
表(别名b
,右表 ),保留左表(names
)的所有记录 :- 右表(
ages
)有匹配id
的行,就关联对应数据; - 右表无匹配行,右表字段用
NULL
填充 。
- 右表(
- 关键语法:
left join
:左连接关键字,确保左表所有记录出现在结果,右表按需匹配或补NULL
。on a.id=b.id
:关联条件,指定用id
字段匹配两张表的记录 。
3.2右连接(right join)
以右表数据为准,关联左表匹配行;左表无匹配时,用 NULL
填充左表字段,且左、右连接可通过调换表顺序相互转化
-- 3、right join(右关联)
select * from
names as a
right join
ages as b
on a.id=b.id;
- 功能:用
RIGHT JOIN
关联names
表(别名a
,左表 )和ages
表(别名b
,右表 ),保留右表(ages
)的所有记录 :- 左表(
names
)有匹配id
的行,就关联对应数据; - 左表无匹配行,左表字段用
NULL
填充 。
- 左表(
- 关键语法:
right join
:右连接关键字,确保右表所有记录出现在结果,左表按需匹配或补NULL
。on a.id=b.id
:关联条件,指定用id
字段匹配两张表的记录 。
3.3内连接(inner join)
只保留两表能匹配(交集 )的行,类似 “共同满足条件的数据”,举例 “取有相同兴趣爱好的记录” 可用内连接
-- 1、inner join(内关联)
-- 两边都需要有数据才能关联到
select * from
names as a
inner join
ages as b
on a.id=b.id;
- 功能:用
INNER JOIN
关联names
表(别名a
)和ages
表(别名b
),只保留 两表id
字段匹配 的记录(即两边都有对应id
数据的行才会出现在结果 )。 - 关键语法:
inner join
:内连接关键字,取两表满足ON
条件的交集数据 。on a.id=b.id
:关联条件,指定用id
字段匹配两张表的记录 。