查询语句知识点汇总
一、基础查询
基本语法为:
SELECT 查询列表 FROM 表名
其中,查询列表可以是:表中字段、常量值、表达式、函数;查询的结果是一个虚拟的表格。
一些小tips:
1.可以用着重号 ''来区分字段和关键字
2.经常会使用DISTINCT 去重
3.起别名:既可以使用as,也可以使用空格
# 使用as起别名
SELECT * FROM employees AS e;
# 使用空格起别名
SELECT * FROM employees e;
4.+的作用:运算符
- 两个操作数都为数值型,则做加法运算
- 若其中一方为字符型,则尝试将字符型数值转换成数值型,转换成功则继续做运算,失败则转换为0
- 只要一方为null,结果肯定为null
二、条件查询
SELECT 查询列表 第③步
FROM 表名 第①步
WHERE 筛选条件 第②步
筛选条件分类:
- 按条件表达式筛选:=、!=、<>、<=、>=、<、>
- 按逻辑表达式筛选:&&(and) 、||(or)、!(not)
- 模糊查询:
- like:通常搭配通配符(%代表任意多个字符,包含0个字符;_代表任意单个字符)
- between and :包含临界值
- in :列表内的值类型必须一致或兼容
- is null、is not null
# 查询名字中带字母a的员工的所有信息
SELECT * FROM employees
WHERE last_name LIKE '%a%';
# 查询员工名中第二个字符为_的员工名
SELECT last_name FROM employees
WHERE last_name LIKE '_\_%'; # 这里的\起到的是转义的作用
三、排序查询
SELECT 查询列表 第③步
FROM 表名 第①步
[WHERE 筛选条件] 第②步
ORDER BY 排序列表 [DESC/ASC] 第④步
特点:
1.默认asc升序排列
2.order by 支持单个字段、多个字段、表达式、别名、函数
3.order by一般放在查询语句的最后面,LIMIT子句除外
四、常见函数
需要掌握函数名、函数功能以及具有使用方法。
4.1 单行函数
4.1.1 字符函数
- length():获取参数值的字节个数
值得注意的是,该函数获取的是字节的个数;utf8下一个英文字母占1个字节,一个汉字占3个字节;gbk下一个汉字占2个字节
SELECT LENGTH('MySQL');
# 5
# 查看字符集
SHOW VARIABLES LIKE ‘%char%’;
# 若客户端的字符集为utf8
SELECT LENGTH('数据库');
# 9
- concat(str1, str2, …) :拼接
# 将员工的first_name和last_name用‘_’进行连接
SELECT CONCAT('first_name','_','last_name') output
FROM employees;
- upper()/lower():将字符串中的小/大写全转换成大/小写
SELECT UPPER('mysQL') output;
# MYSQL
SELECT LOWER('MYSQL') output;
# mysql
- substr/substring(str,起始索引,截取长度):截取指定索引下的字符
这里,有一点需要注意一下:索引是从1开始的
SELECT SUBSTR('查询语句知识点汇总',1,4) output;
# 查询语句
SELECT SUBSTR('查询语句知识点汇总',8) output;
# 汇总
- instr(str1,str2):返回子串第一次出现的索引,如果找不到,返回0
SELECT INSTR('查询语句知识点汇总','汇总') output;
# 7
- trim():去除前后空格,也可去除指定字符
SELECT TRIM('a' from ' 数据库 ') output;
# 数据库
SELECT TRIM('a' from 'aaaa数aaaa据库aaaa') output;
# 数aaaa据库
SELECT TRIM('a' from 'aaaa数aaaa据库AAAAA') output;
# 数aaaa据库AAAAA
- lpad(str,指定长度,指定字符):用指定的字符实现左填充,使填充后的字符串达到指定长度,rpad同理
SELECT LPAD('数',3,'*') output;
# 数**
SELECT LPAD('数据库',2,'*') output;
# 数据
- replace(str,需要被替换的字符串,替换的字符串):替换
SELECT REPLACE('我爱学习','学习','看电视') output;
# 我爱看电视
4.1.2 数学函数
- rand():获取随机数,返回0-1之间的小数
SELECT RAND();
# 0.36748154868137234
- round():四舍五入
# 保留两位小数
SELECT ROUND(1.567, 2);
# 1.57
- ceil():向上取整,返回>=该参数的最小整数
floor():向下取整,返回<=该参数的最大整数
# 在0-99间随机抽取一个整数
SELECT FLOOR(RAND()* 100);
# 在1-100间随机抽取一个整数
SELECT CEIL(RAND()* 100);
- truncate():截断
SELECT TRUNCATE(1.699999,1);
# 1.6
- mod(a, b):取余;a为被除数,b为除数。等价于%
mod(a, b) = a - a/b*b
SELECT mod(10,3);
# 1
SELECT mod(-10,-3); # -10-(-10)/(-3)*(-3) = -1
# -1
SELECT mod(10,-3); # 10-(10)/(-3)*(-3) = 1
# 1
4.1.3 日期函数
- now(): 返回当前系统的日期+时间
- curdate(): 返回当前系统的日期,不包含时间
- curtime(): 返回当前时间
SELECT NOW();
# 2020-08-05T08:32:45Z
SELECT CURDATE();
# 2020-08-05
SELECT CURTIME();
# 08:35:20
- year()/month()/day()/hour()/minute()/second():获取指定部分的年/月/日/小时/分钟/秒
SELECT MONTH(NOW()) 月;
# 8
SELECT MONTHNAME(NOW()) 月; #monthname以英文的形式返回月
# August
- str_to_date(): 将日期格式的字符转换成指定格式的日期
(字符串转换成日期)
SELECT STR_TO_DATE('8-20-2050','%m-%d-%Y');
# 2050-08-20
- date_format(): 将日期按指定的格式转换成字符
(日期转换成字符串)
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') output;
# 20年08月05日
SELECT DATE_FORMAT(NOW(),'%Y-%m-%d') output;
# 2020-08-05
- datediff(date1, date2): 计算两个日期之间的天数
其中,datediff()函数对时间差值的计算方式为date1-date2
SELECT DATEDIFF('2020-06-05','2020-08-05') output;
# -61
4.1.4 其他函数
- version(): 查看MySQL的版本类型,等价于
SHOW VARIABLES LIKE 'VERSION';
- database(): 查看当前所使用的数据库
- user(): 查看当前登录的用户
4.1.5 流程控制函数
- if 函数
SELECT IF(10>5, '大', '小');
# 大
- case 函数
case可以搭配select充当表达式,也可以单独作为语句。
- 等值判断
CASE 要判断的字段或表达式
WHEN 常量1 THEN 要显示的值1或语句1
WHEN 常量2 THEN 要显示的值2或语句2
......
[ELSE 要显示的值n或语句n] <--- 可省略
END
【例】
SELECT salary 原始工资, department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END 新工资
FROM employees;
- 区间判断
CASE
WHEN 条件1 THEN 要显示的值1或语句1
WHEN 条件2 THEN 要显示的值2或语句2
......
ELSE 要显示的值n或语句n
END
【例】
CASE
WHEN score >= 90 THEN '优秀'
WHEN score >= 70 THEN '良好'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END
4.2 分组函数 (也称为聚合函数/统计函数/组函数)
sum() | avg() | max() | min() | count() |
---|---|---|---|---|
求和 | 求平均 | 求最大值 | 求最小值 | 统计个数 |
特点:
- sum()、avg()一般用于处理数值型;而max()、min()、count()可处理任何类型
- 以上5个函数都忽略null值
- 均可以与distinct搭配使用
- 统计总行数:
SELECT COUNT(*) FROM table1;
或者SELECT COUNT(常量值,一般为1) FROM table1;
- 和分组函数一同查询的字段有限制:和分组函数一同查询的字段要求是group by后的字段
五、分组查询
SELECT 分组函数, 列(要求出现在group by的后面) 第④步
FROM 表名 第①步
[WHERE 筛选条件] 第②步
GROUP BY 需要分组的列 第③步
[ORDER BY子句] 第⑤步
特点:
- 分组查询中的筛选条件分为两类
数据源 | 位置 | 关键字 | |
---|---|---|---|
分组前筛选 | 原始表 | group by前 | where |
分组后筛选 | 分组后的结果集 | group by后 | having |
- 分组函数条件肯定是放在having子句中
- 能用分组前筛选的尽量在分组前筛
- group by子句支持单个、多个字段分段(逗号隔开,没有顺序)
六、连接查询(多表查询)
6.1 笛卡尔乘积现象
当查询的字段来自于多个表时,就会用到连接查询。
当表与表之间没有建立有效的连接条件时,就会出现笛卡尔乘积现象,即表1m行,表2n行,结果将产生一个么m*n行的表。
下面举个例子,大家来直观地感受一下笛卡尔乘积现象。
假设有两张表,具体如下:
表6.1: 人物表authors
id | author_name | sex | born_date | book_id |
---|---|---|---|---|
1 | 林徽因 | 女 | 1904-6 | 2 |
2 | 杨绛 | 女 | 1911-7 | 1 |
表6.2: 作品表books
id | book_name | publishing_date |
---|---|---|
1 | 干校六记 | 1981 |
2 | 你是人间四月天 | 2005 |
现在想要查询作者及其作品名,我们期望得到的是:
author_name | book_name |
---|---|
林徽因 | 你是人间四月天 |
杨绛 | 干校六记 |
但如果直接使用SELECT author_name, book_name FROM authors,books;
对两个表进行查询,就相当于拿着表1的每一行记录去依次匹配表2的每一行记录,则会得到:
author_name | book_name |
---|---|
林徽因 | 你是人间四月天 |
林徽因 | 干校六记 |
杨绛 | 你是人间四月天 |
杨绛 | 干校六记 |
结果显然与实际不符,这就是一个典型的笛卡尔乘积错误现象。
6.2 连接查询的分类
按功能分类:
- 内连接:等值连接、非等值连接、自连接
- 外连接:左外连接、右外连接、全外连接(MySQL不支持)
- 交叉连接
按年代分类:
- sql92标准(只支持内连接)
- sql99标准
sql92 vs sql99:
功能:99支持的功能更多
可读性:99实现了连接条件和筛选条件的分离,可读性较高
因此,一般较为推荐使用99语法。
6.2.1 sql92标准语法
我们先简单了解一下92语法:
SELECT 查询列表
FROM 表1名, 表2名
WHERE 连接条件;
接着,直接来看例子吧,先来个等值连接的。
沿用表6.1、6.2的数据,我们现在想要查询作者及其作品名,则可以写成:
SELECT a.author_name, b.book_name
FROM authors a,books b
WHERE a.book_id = b.id;
是不是很容易?
再来看一个非等值连接的例子。
依旧假设有两张表,一张是学生成绩表grade,一张是成绩等级表grade_level,具体内容如下:
表6.3: 学生成绩表grade
stu_name | grade |
---|---|
小明 | 98 |
小刚 | 86 |
小强 | 72 |
小新 | 48 |
表6.4: 成绩等级表grade_level
grade_level | lowest_grade | highest_grade |
---|---|---|
A | 90 | 100 |
B | 70 | 89 |
C | 60 | 69 |
D | 0 | 59 |
现在我们想要查询每个学生的成绩及其等级,这就用到了非等值查询,我们可以写:
SELECT stu_name,grade,grade_level
FROM grade g, grade_level gl
WHERE grade BETWEEN gl.lowest_grade AND gl.highest_grade;
即可得到结果如下:
stu_name | grade | grade_level |
---|---|---|
小明 | 98 | A |
小刚 | 86 | B |
小强 | 72 | B |
小新 | 48 | D |
最后,来看看自连接。
这次只有一张表:员工表employees,表中有三个字段:employee_ id员工编号、name员工姓名、manager_id上级经理编号。现在想要查询的是员工名,以及其上级经理的名字。
employee_ id | name | manager_id |
---|---|---|
1 | 小红 | NULL |
2 | 小橙 | 1 |
3 | 小黄 | 1 |
4 | 小绿 | 3 |
5 | 小青 | 2 |
6 | 小蓝 | 4 |
7 | 小紫 | 3 |
想要查询员工上级的名字,即先找到员工对应上级的编号manager_id(第一次用员工表employees,记作表1)。找到对应上级编号manager_id后,拿着这个编号去比对员工编号employee_ id和name(第二次用员工表employees,记作表2),这就涉及到一表复用,即自连接。
我们可以看到,在第一次用员工表employees时,该表中的employee_ id被看作的是普通员工编号;在第二次用这个表的时候,是将表中的employee_ id当作是担任上级的员工编号。而这两张表之间的连接条件就在于,表1中的上级编号manager_id等于表2中的员工编号employee_ id。
所以,我们可以得到具体过程,如下:
SELECT e.name 员工名, m.name 领导名
FROM employees e, employees m #此处可以看出起别名的好处,可以清楚地看到我们将前表当成的是普通员工表,后一张表代表的是经理表
WHERE e.manager_id = m.employee_id;
最终结果为:
员工名 | 领导名 |
---|---|
小橙 | 小红 |
小黄 | 小红 |
小绿 | 小黄 |
小青 | 小橙 |
小蓝 | 小绿 |
小紫 | 小黄 |
6.2.2 sql99标准语法
看完了92语法,下面我们瞅瞅99语法下的连接查询,其具体语法如下:
SELECT 查询列表 第⑦步
FROM 表1 别名 第①步
[连接类型] JOIN 表2 别名 第②步
ON 连接条件 第③步
WHERE 筛选条件 第④步
GROUP BY 分组 第⑤步
HAVING 筛选条件 第⑥步
ORDER BY 排序列表 第⑧步
语法中的连接类型主要如下:
1.内连接:inner
- 等值连接(inner可省略)
- 非等值连接
- 自连接
由于上面在92语法中分别举过例子,只是语法上有些许变化,这里就不赘述了。
接下来,一起来看一下92语法并不支持的外连接。
2.外连接:用于查询一个表中有但一个表中没有的记录。 那个有记录的表为主表,没有记录的则为从表。
- 左外连接:left [outer]
- 右外连接:right [outer]
- 全外连接:full [outer] (MySQL不支持)
特点:
1.外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null值。所以,可以列出公式:外连接查询的结果 = 内连接结果+主表中有而从表中没有的结果
2.left左边的是主表,right右边的是主表
3.交换左外连接表的顺序,效果与右外连接一致,右外连接同理
4.全外连接 = 内连接的结果+表1中有但表2没有的结果+表2中有但表1中没有的结果
老规矩,继续上个例子吧。
继续以作家6.1和作品6.2这两张表为例,先将这两张表扩充点记录吧,如下:
表6.1(续): 人物表authors
id | author_name | sex | born_date | book_id |
---|---|---|---|---|
1 | 林徽因 | 女 | 1904-6 | 2 |
2 | 杨绛 | 女 | 1911-7 | 1 |
3 | 毕淑敏 | 女 | 1952-10 | 3 |
4 | 铁凝 | 女 | 1957-9 | 6 |
5 | 严歌苓 | 女 | 1958-11 | 9 |
6 | 迟子建 | 女 | 1964-2 | 4 |
表6.2(续): 作品表books
id | book_name | publishing_date |
---|---|---|
1 | 干校六记 | 1981 |
2 | 你是人间四月天 | 2005 |
3 | 蓝色天堂 | 2011 |
4 | 群山之巅 | 2015 |
现想要查询作品 不在作品表books中 的作家的名字。
我们在做外连接的时候,最关键的一点就在于确定哪张表是主表。在这道题中很容易看出人物表为主表,如果一下子看不出的小伙伴,可以记住一个技巧:找题目的中心词,看最终想要查询的对象是谁。你要查询的信息主要来自于哪个表,谁就是主表。
以这个例题为例,我们最终想要查询的是作家的名字,而作家的名字显然是来自于人物表authors,所以根据我们上面所说的你要查询的信息主要来自于哪个表,谁就是主表可以确定人物表authors为主表。
确定完主表authors,若我们选择左外连接,就将该表放在join左边,若选择的是右外连接,就将其放在右边。
写好连接条件后,即得到authors表中字段作家名 author_name的所有记录,根据之前提及到的外连接特点1可知,作品表books中有和人物表authors匹配的,则显示匹配的值,若无,则返回null。得到结果如下:
author_name | id | book_name |
---|---|---|
林徽因 | 2 | 你是人间四月天 |
杨绛 | 1 | 干校六记 |
毕淑敏 | 3 | 蓝色天堂 |
铁凝 | NULL | NULL |
严歌苓 | NULL | NULL |
迟子建 | 4 | 群山之巅 |
通过这个大致的结果表可以更加直观地看到上述的结果。第一列字段来自主表authors,保留着其中的所有条记录;后两列字段均来自从表books,在连接条件下,有匹配的字段显示相应的对应值,没有找到匹配字段的呢,则整条记录显示为null。
有了以上的结果,再来看题目想让我们查询的东西:作品不在作品表里 的作家。换种说法就是在人物表中匹配不到的作家,显而易见,一个is null就可以解决了。
重头梳理一遍,我们可以得到整个查询过程,如下(以左外连接为例,右外连接同理):
SELECT a.author_name
FROM authors a
LEFT [OUTER] JOIN books b #outer一般可省略
ON a.book_id = b.id
WHERE b.id IS NULL;
3.交叉连接: cross
交叉连接,相当于6.1所说的笛卡尔乘积,这里也不再赘述了。
以上,已解锁全部的连接查询内容啦~
七、子查询
下面我们一起来看看,查询语句中较为复杂点的一类查询:子查询。
含义:出现在其他语句中的select语句,称为子查询或内查询。
这里的其他语句是指,子查询不仅仅可以用在select语句中,增删改中均可以使用。
与子查询(或称为内查询)相对应,外部的查询语句(即包含着子查询的语句)称为主查询(或称为外查询)。
子查询的分类:
1.按结果集的行列数不同:
- 标量子查询(结果集只有一行一列)
- 列子查询(结果集只有一列多行)
- 行子查询(结果集有一行多列或多行多列)
- 表子查询(结果集一般为多行多列)
2.按照子查询出现的位置:
- select 后面:仅仅支持标量子查询
- from 后面:支持表子查询
- where或having后面:支持标量子查询、列子查询、行子查询(不常用)
- exists后面(相关子查询):支持表子查询
7.1 where或having后面
由于where或having后面接的子查询更多常见普遍,是子查询中的重点,因此,我们就首先来看看放在该关键字后的子查询。
特点:
1.子查询放在小括号内
2.子查询一般放在条件的右侧
3.标量子查询,一般搭配着单行操作符(<、>、>=、<=、=、<>)使用
列子查询,一般搭配着多行操作符(IN、ANY/SOME、ALL)使用
4.子查询的执行优于主查询的执行,主查询的条件用到了子查询的结果
太抽象?看个例子叭。
【 例1 - 标量子查询 】
以表6.3学生成绩表grade为数据,查询谁的成绩比小强高。
表6.3:学生成绩表grade
stu_name | grade |
---|---|
小明 | 98 |
小刚 | 86 |
小强 | 72 |
小新 | 48 |
首先第一步,我们需要查询出小强的成绩:
SELECT grade
FROM grade
WHERE stu_name = '小强'
很容易得到结果,为:
grade |
---|
72 |
可以看到,得到的结果集一行一列,这就是我们所说的标量子查询。
接着,我们需要查询哪个学生的成绩大于我们上一步所查到的成绩,可以这样写:
SELECT stu_name
FROM grade
WHERE grade > (
SELECT grade
FROM grade
WHERE stu_name = '小强'
);
这就是一个子查询。
【 例2 - 标量子查询 】
再来看一个用在having后面的子查询的例子。
假设有一张成绩表,包含学生名、班级名、成绩等字段,具体如下:
表7.1: 成绩表grades
stu_name | class_id | score |
---|---|---|
小七 | 2 | 98 |
小宝 | 2 | 91 |
小琉 | 1 | 86 |
小璃 | 3 | 72 |
小塔 | 1 | 65 |
小小 | 3 | 72 |
小宗 | 3 | 72 |
小主 | 1 | 65 |
我们现在想要查询,最低分大于一班最低分的班级id和其最低分。
第一步,我们应该查出一班的最低分是多少,这肯定是个一行一列的值叭,查询如下:
SELECT MIN(score)
FROM grades
WHERE class_id = 1
第二步,我们是不是应该查询每个班级的最低分,即:
SELECT MIN(score)
FROM grades
GROUP BY class_id
最后,我们基于第二步的结果进行筛选,筛选出最低分大于第一步的结果,查询如下:
SELECT class_id, MIN(score)
FROM grades
GROUP BY class_id
HAVING MIN(score) > (
SELECT MIN(score)
FROM grades
WHERE class_id = 1
);
【例3 - 列子查询】
在上面特点3中提到,列子查询,一般搭配着多行操作符(IN、ANY/SOME、ALL)使用。所以在看例子前,我们先来简单看一下多行比较操作符的含义是啥。
多行比较操作符 | 含义 |
---|---|
IN / NOT IN | 等于列表中的任意一个 |
ANY / SOME | 和子查询返回的某一个值比较 |
ALL | 和子查询返回的所有值比较 |
懵懵的?来看道题吧。
表7.2: 学生表students
stu_id | stu_name | class_id | score |
---|---|---|---|
202001 | 小三 | 2 | 98 |
202002 | 小五 | 5 | 86 |
202003 | 小奥 | 1 | 97 |
202004 | 小荣 | 3 | 72 |
202005 | 小竹 | 1 | 94 |
202006 | 小戴 | 3 | 94 |
202007 | 小俊 | 4 | 81 |
202008 | 小娜 | 2 | 96 |
202009 | 小清 | 1 | 98 |
202010 | 小昊 | 3 | 87 |
202011 | 小银 | 4 | 66 |
表7.3: 班级评级表class_level
class_id | level |
---|---|
1 | A |
2 | B |
3 | A |
4 | C |
5 | B |
查询A类班或B类班所有学生的姓名。
第一步,我们先要查到A类班或B类班的班级编号class_id。
SELECT class_id
FROM class_level
WHERE level IN ('A','B')
不难看出,以上查询得到的结果为一列多行,即包含一个字段class_id,多行记录1,2,3,5。这就是上面所说的列子查询。
第二步,基于第一步的结果,查询结果集班级中所有学生的姓名。
SELECT stu_name
FROM students
WHERE class_id IN (
SELECT class_id
FROM class_level
WHERE level IN ('A','B')
);
这就是一个简单的列子查询。
【例4 - 列子查询】
以表7.2为数据,继续来看道题。
查询其他班级中比三班任一学生成绩低的学生的姓名及成绩。
第一步,先查三班所有学生的成绩。
SELECT DISTINCT score
FROM students
WHERE class_id = 3
第二步,再查学生名、成绩,要求成绩小于any(三班所有学生的成绩,即第一步的结果)。
SELECT stu_name, score
FROM students
WHERE score < ANY(
SELECT DISTINCT score
FROM students
WHERE class_id = 3
) AND class_id <> 3;
最终的查询结果为:
stu_name | score |
---|---|
小五 | 86 |
小俊 | 81 |
小银 | 66 |
当然,此处主要是为了练习一下any的用法, 其实我们也可以不用any,只要去查询score小于三班最高分的学生即可。
7.2 select后面
select后面只支持标量子查询,直接来看道题吧。
依旧以表7.2和7.3为源数据,查询各等级的下的学生人数。
第一步,查询班级的等级和学生人数。其中班级等级来源于表class_level,学生人数来源于表students,我们可以写出框架,如下:
SELECT cl.*, (
SELECT COUNT(*)
FROM students
) 学生人数
FROM class_level cl
运行一下,我们可以得到结果:
class_id | level | 学生人数 |
---|---|---|
1 | A | 11 |
2 | B | 11 |
3 | A | 11 |
4 | C | 11 |
5 | B | 11 |
显然,学生人数的值是错误的,我们想要得到的是各等级下的学生人数,而现在返回的是总人数。出现错误的原因是,我们的子查询中并没有建立起与主查询的联系。所以,我们接下来要做的是,在子查询里面做一个筛选,让我查询的班级号正好等于等级表中的班级。
SELECT cl.*, (
SELECT COUNT(*)
FROM students s
WHERE s.class_id = cl.class_id
) 学生人数
FROM class_level cl;
再次运行一下,我们可以得到结果:
class_id | level | 学生人数 |
---|---|---|
1 | A | 3 |
2 | B | 2 |
3 | A | 3 |
4 | C | 2 |
5 | B | 1 |
嗯?距离我们想要的结果还差一点。
现在得到的学生人数还只是每个班级下的人数,我们想要得到的是各等级下的人数,所有还需要将学生人数按照等级进行汇总。
SELECT level, SUM((
SELECT COUNT(*)
FROM students s
WHERE s.class_id = cl.class_id
)) 学生人数
FROM class_level cl
GROUP BY level;
可得到最终结果:
level | 学生人数 |
---|---|
A | 6 |
B | 3 |
C | 2 |
7.3 from后面
from后面子查询得到的结果集,实际上就是被当作表来使用,因此必须起别名。具体几行几列都是可以的,主要是看需求。
继续来看个例子。
此处,我们需要用到表7.2和表6.4。
查询每个班级平均分的等级。
表7.2: 学生表students
stu_id | stu_name | class_id | score |
---|---|---|---|
202001 | 小三 | 2 | 98 |
202002 | 小五 | 5 | 86 |
202003 | 小奥 | 1 | 97 |
202004 | 小荣 | 3 | 72 |
202005 | 小竹 | 1 | 94 |
202006 | 小戴 | 3 | 94 |
202007 | 小俊 | 4 | 81 |
202008 | 小娜 | 2 | 96 |
202009 | 小清 | 1 | 98 |
202010 | 小昊 | 3 | 87 |
202011 | 小银 | 4 | 66 |
表6.4: 成绩等级表grade_level
grade_level | lowest_grade | highest_grade |
---|---|---|
A | 90 | 100 |
B | 70 | 89 |
C | 60 | 69 |
D | 0 | 59 |
第一步,查询每个班级的平均分。
SELECT AVG(score),class_id
FROM students
GROUP BY class_id
第二步,将第一步的结果集与成绩等级表grade_level连接,筛选条件为平均分between最低分lowest_grade and 最高分highest_grad,即可得到我们想要的结果,具体查询过程如下:
SELECT avg_score.class_id, gl.grade_level
FROM (
SELECT AVG(score) ag, class_id
FROM students
GROUP BY class_id
) avg_score #必须起别名,否则后面如何表示这个结果集,如何知道是这个“表”
INNER JOIN grade_level gl
ON avg_score.ag BETWEEN gl.lowest_grade AND gl.highest_grade;
7.4 exists后面(相关子查询)
具体的语法:exists(完整的查询语句)
结果:0 或 1
作用:判断查询结果中是否有值,有则返回1,没有则返回0
需要注意一下的是,不同于之前的三种子查询,exists后面的子查询的执行是后于主查询的,是对主查询结果的一个筛选。
来看个例子吧,此处使用表6.1(续)、6.2(续)的数据。
表6.1(续): 人物表authors
id | author_name | sex | born_date | book_id |
---|---|---|---|---|
1 | 林徽因 | 女 | 1904-6 | 2 |
2 | 杨绛 | 女 | 1911-7 | 1 |
3 | 毕淑敏 | 女 | 1952-10 | 3 |
4 | 铁凝 | 女 | 1957-9 | 6 |
5 | 严歌苓 | 女 | 1958-11 | 9 |
6 | 迟子建 | 女 | 1964-2 | 4 |
表6.2(续): 作品表books
id | book_name | publishing_date |
---|---|---|
1 | 干校六记 | 1981 |
2 | 你是人间四月天 | 2005 |
3 | 蓝色天堂 | 2011 |
4 | 群山之巅 | 2015 |
想要查询有作品的人物名,我们可以这样写:
SELECT author_name
FROM authors a
WHERE EXISTS(
SELECT *
FROM books b
WHERE a.book_id = b.id
);
当然,我们也可以用in来替代exists,如下:
SELECT author_name
FROM authors a
WHERE a.book_id IN (
SELECT b.id
FROM books b
);
以上,已解锁全部的子查询内容啦~
八、分页查询
当要显示的数据一页显示不全时,就需要分页提交sql请求。
SELECT 查询列表 第⑦步
FROM 表1 第①步
[连接条件] JOIN 表2 第②步
ON 连接条件 第③步
WHERE 筛选条件 第④步
GROUP BY 需要分组的列 第⑤步
HAVING 筛选条件 第⑥步
ORDER BY子句 第⑧步
LIMIT [offset],size 第⑨步
# offset表示的是:要显示条目的索引数(从0开始)
# size表示的是:要显示的条目个数
特点:
1.limit语句放在查询语句的最后
2.要显示N页,已知每页的条目数为size,则可使用公式LIMIT (N-1)*size ,size;
九、联合查询
当我们想要查询的结果来自于多个表,且多个表没有直接的连接关系,但是查询的信息是一致的时候,我们通常会使用联合查询。使用关键字union将多条查询语句的结果合并成一个结果。具体语法如下:
查询语句1
UNION
查询语句2
UNION
...
特点:
1.要求多条查询语句的查询列数是一致的
2.要求多条查询语句查询的每一列的类型和顺序最好一致
3.union关键字默认去重,如果使用union all 可以包含重复项
下面再一起来看个例子,近距离感受一下联合查询的魅力吧。假设现有两个班级的成绩表,如下:
表9.1: 一班的成绩表class1
stu_id | stu_name | sex | grade |
---|---|---|---|
1 | 小明 | 男 | 98 |
2 | 小黄 | 女 | 91 |
3 | 小刚 | 男 | 86 |
4 | 小强 | 男 | 72 |
5 | 小知 | 男 | 65 |
6 | 小新 | 女 | 48 |
表9.2: 二班的成绩表class2
id | s_name | score |
---|---|---|
01 | 小红 | 100 |
02 | 小黄 | 91 |
03 | 小蓝 | 84 |
04 | 小绿 | 71 |
想要查询一下这两个班成绩在90分以上的学生,给出姓名与成绩
SELECT stu_name, grade FROM class1 WHERE grade > 90
UNION
SELECT s_name, score FROM class2 WHERE score >90;
查询结果如下:
stu_name | grade |
---|---|
小明 | 98 |
小黄 | 91 |
小红 | 100 |
细心的小伙伴会注意到,一班和二班的学生名和成绩所使用的字段名是不一样的,在结果中使用的是一班的字段名作为联合表的字段名。所以,可以看见,在使用union进行联合查询时,字段名是默认为第一条语句的字段名的。
此外,还有小伙伴可能还会发现,一班和二班都有个叫小黄的学生,并且成绩都超过90分,但是在结果中却只出现了一个小黄的记录。所以,还有一点要记住的是,union是默认去重的。当我们需要包含重复项时,可以选择使用union all关键字,语法类似,比如说这里我们应该写成:
SELECT stu_name, grade FROM class1 WHERE grade > 90
UNION ALL
SELECT s_name, score FROM class2 WHERE score >90;
好了,以上便是DQL数据查询语言中最最基础的全部内容啦~
最后,给大家推荐一个SQL在线练习平台:http://sqlfiddle.com/。以上所有内容均可以在这个平台上,自行创建表运行查看。无需注册简单易操作,支持多类SQL语言,是个宝藏网站啦,盖戳~
附该网页图: