MySQL高级查询操作


前言

查询语句书写顺序:
1、select
2、from
3、where
4、group by
5、having
6、order by
7、limit

查询语句执行顺序:
数据库的时候先执行from,确定数来自哪张表
再执行where,看看哪些行需要查询
再执行group by,确定是否分组
再执行having,过滤掉不要的组
然后是select,再确定查询哪些列
随后是order by排序
最后是limit限定

在这里插入图片描述

聚集函数

聚集函数(也叫聚合函数),方便用户统计一些数据。聚合查询是对列的值进行计算,然后返回一个单一的值。

COUNT(*): 统计表中元组个数;
COUNT(列名):统计本列列值个数;
SUM(列名):计算列值总和(必须是数值型列);
AVG(列名):计算列值平均值(必须是数值型列);
MAX(列名):最大、最高;
MIN(列名):最小、最低。

max和min可以统计数字型数据、字符型数据、日期型数据

【例】查询最高最低的学生成绩

SELECT MAX(score),MIN(score)
FROM stu

sum和avg只适用于数字型数据

【例】统计全部学生的平均成绩

SELECT AVG(score) 
FROM stu

count(*)返回表中满足条件的行数
其中*也可以写具体的列名,但是空值不统计

示例:
【例】统计老师的总人数

SELECT COUNT(*) 
FROM teacher

查询工作在HK的员工人数,最高工资及最低工资

SELECT count(*) AS 员工人数,max(sal) AS 最高工资,min(sal) AS 最低工资
FROM emp e JOIN dept d 
ON e.deptno = d.deptno 
WHERE loc='HK';
  1. 查询学生表的总人数:

    SELECT COUNT(*) FROM student;
    
  2. 查询学生年龄大于18岁的总人数:

    SELECT COUNT(*) FROM student WHERE age > 18;
    
  3. 查询class_id=1的所有学生年龄总和:

    SELECT SUM(age) FROM student WHERE class_id = '1';
    
  4. 查询学生最大年龄:

    SELECT MAX(age) FROM student;
    
  5. 查询学生最小年龄:

    SELECT MIN(age) FROM student;
    
  6. 求学生年龄的平均值:

    SELECT AVG(age) FROM student;
    

聚合函数不能出现在 WHERE 子句中

分组查询:GROUP BY

GROUP BY 子句用于将具有相同值的行分组,并通常与聚合函数(如 COUNT(), SUM(), AVG() 等)一起使用来执行汇总计算。

当有每个、每组的时候需要分组

SQL语句中使用了分组查询group by,在select子句中就要使用分组统计函数
select语句中查询分组的列

【例】查每个班级编号,平均成绩,进行分组

SELECT classid,AVG(score)
FROM stu
GROUP BY classid; 

【例】统计每个出版社的出版图书的数目
分析:如果能够将所有的图书,按照出版社的名称进行分组,然后我们在统计每一组的元组的个数,我们就能能到得到期望的数据。

比如book表中字段有:bookid,name,author,public,price

可以使用GROUP BY <列名>进行分组

<列名>上值相同的元组被分在一组,该列称为分组依据列。
然后可以使用聚集函数统计每一组的数据。

SELECT COUNT(*) , publish FROM book
GROUP BY publish

【例】统计每个人所借图书的数目。

SELECT COUNT(bookid), Reader_id
FROM Borrow 
GROUP BY Reader_id
Having COUNT(bookid)>2

【例】统计每个出版社出版图书的平均价格,并显示每个出版社的名称

SELECT publish, AVG(price) AS 平均价格
FROM book
GROUP BY publish

【例】统计每个岗位和部门都相同的人数

SELECT deptno,job,COUNT(*) AS 人数
FROM emp
GROUP BY deptno,job;

示例:

  1. 按班级ID分组并统计每个班级的学生人数:

    SELECT class_id AS 班级, COUNT(*) AS 人数 
    FROM student 
    GROUP BY class_id;
    
  2. 按地址分组并统计每个地址的学生人数:

    SELECT address, COUNT(*) AS 人数 
    FROM student 
    GROUP BY address;
    

注意:

  1. SELECT 语句中,除了聚合函数(如 COUNT(*)),只能包含在 GROUP BY 子句中指定的列。
  2. 分组之后对统计结果进行条件筛选必须使用 HAVING,不能使用 WHERE
    • WHERE 用于过滤行,而 HAVING 用于过滤分组。
  3. HAVING语句通常与GROUP BY语句联合使用,用来过滤由GROUP BY语句返回的记录集

过滤:HAVING

HAVING必须写在GROUP BY后面,ORDER BY前面,HAVING后面也是写条件的(和where相近)

HAVING语句通常与GROUP BY语句联合使用,用来过滤由GROUP BY语句返回的记录集

WHERE子句中编写的是过滤筛选数据行的条件
HAVING子句是使用分组统计函数的

语法格式:

SELECT 字段1,字段2.....
FROM 表名 GROUP BY 分组字段 HAVING 分组条件;

【例】查询出版图书平均价格高于30元的出版社名称,并显示其图书平均价格。

SELECT publish,AVG(price) FROM book 
GROUP BY publish
HAVING AVG(price)>30

【例】统计每个班级的学生人数,并只显示人数大于等于5的班级

SELECT class_id AS 班级, COUNT(*) AS 人数 
FROM student 
GROUP BY class_id 
HAVING COUNT(*) >= 5;

【例】查询出版图书多于2本的出版社名称和出版图书数目

SELECT publish, COUNT(*) FROM book 
GROUP BY publish
HAVING COUNT(*)>2

【例】查询部门编号,岗位,平均工资,其中岗位在销售、经理、职员之中,把岗位和部门都相同的分在一组,保留平均工资高于1000的,平均工资按降序排列

SELECT deptid,job,AVG(sal) AS 平均工资
FROM emp
WHERE job in('salesman','manager','clerk') 
GROUP BY deptid,job
HAVING avg(sal)>1000
ORDER BY 平均工资 DESC; 

【例】查询部门人数大于2的部门编号,部门名称,部门人数。
员工表内字段:deptid
部门表内字段:deptid,dname

SELECT d.deptid,dname,count(*)
FROM emp e JOIN dept d 
ON e.deptid = d.deptid
GROUP BY d.deptno 
HAVING COUNT(*)>2;

【例】查询部门平均工资大于2000,且人数大于2的部门编号,部门名称,部门人数,部门平均工资,并按照部门人数升序排序。
员工表内字段:deptid,sal
部门表内字段:deptid,dname

SELECT d.deptid,dname,COUNT(*),avg(sal)
FROM emp e,dept d
WHERE e.deptid=d.deptid
GROUP BY d.deptid
HAVING avg(sal)>2000
AND COUNT(*)>2
ORDER BY COUNT(*);

嵌套子查询

子查询是指一个查询语句嵌套在另一个查询语句内部的查询(子查询是SELECT嵌套的查询,当条件不知道的时候用子查询)。在SELECT中先计算子查询。子查询的结果可以用来作为外层查询的条件,这样的查询可以基于单个表或者多个表。

子查询中常用的操作符:ANY(SOME)、ALL、IN和EXISTS

  • IN:用于检查某值是否在子查询结果集中。
  • ANY/SOME:意味着只要满足与子查询返回的任何一个值的比较条件即可。例如,> ANY会返回true,如果目标值大于子查询返回的任意一个值。
  • ALL:意味着需要满足与子查询返回的所有值的比较条件。例如,> ALL会返回true,仅当目标值大于子查询返回的所有值时。
  • EXISTS:用于检查子查询是否返回任何行。如果子查询返回一行或多行,则EXISTS条件为真。

NOT IN后面的子查询要确保结果里不能包含空值(IS NOT NULL)

执行顺序:先内层后外层;先查子查询后查主查询

相关子查询运行时和主查询查询的表有关联,使用了主查询表某些列数据

【例】找出所有属于名为“第三期”或“第一期”班级的学生,并返回这些学生信息

EXPLAIN SELECT s.id, s.name, s.age
FROM student s
WHERE s.class_id IN (
    SELECT c.id 
    FROM class c 
    WHERE c.name = '第三期' OR name = "第一期"
);

【例】获取年龄大于18岁且属于“第三期”班级的所有学生的详细信息

SELECT * 
FROM (
    SELECT * FROM student WHERE age > 18
) a1
JOIN (
    SELECT id FROM class WHERE name = '第三期'
) a2 ON a1.class_id = a2.id;
  1. 执行子查询 a1:
    首先执行子查询 (SELECT * FROM student WHERE age > 18),筛选出所有年龄大于18岁的学生记录,并将其结果命名为a1
  2. 执行子查询 a2:
    然后执行子查询 (SELECT id FROM class WHERE name = '第三期'),找出名称为“第三期”的班级ID,并将其结果命名为a2
  3. JOIN 操作:
    a1a2的结果基于条件a1.class_id = a2.id进行连接。这意味着只有那些属于“第三期”班级且年龄大于18岁的学生记录会被包含在最终结果集中。

【例】这个SQL子查询语句用于找出所有年龄最大的学生,并返回他们的id、姓名和年龄。

SELECT id, name, age
FROM student
WHERE age in (SELECT MAX(age) FROM student);

首先通过子查询找到 student 表中的最大年龄
外部查询再根据最大的学生年龄,判断表中数据是否为该最大学生年龄

【例】查询成绩最低的学生姓名和学号

解题思路:先写大框,最后写括号里面的东西(查询所有学生里面最低的成绩),变成填空题写

SELECT sname,stuid
FROM stu
WHERE score=(SELECT MIN(score) FROM stu);

【例】查询入学日期比二班入学日期最早的学生还要早的学生姓名,入学日期

SELECT sname,studate
FROM stu
WHERE studate<(SELECT min(studate) FROM stu WHERE classid=2);

比较运算中使用子查询

【例】查询成绩最好的学生的姓名

SELECT name FROM Student 
WHERE Score =(SELECT MAX(Score) FROM Student)

带有IN的子查询

【例】查询与"C语言"在同一出版社的图书信息

SELECT * FROM Book WHERE publish 
IN (SELECT publish FROM Book WHERE name='C语言')

【例】查询张三所借图书的图书编号

SELECT book_ID FROM Borrow 
WHERE reader_ID IN (SELECT reader_ID FROM Reader WHERE name='张三')

【例】查询"张三"所借的图书的名称

SELECT name FROM Book 
WHERE book_ID IN(SELECT book_ID FROM Borrow 
WHERE reader_ID IN (SELECT reader_ID FROM Reader WHERE  name='张三'))

查询过程:
第1步,查询 “张三” 的reader_ID。
第2步,依据 reader_ID在Borrow表中找张三所借图书的book_ID
第3步,依据 book_ID在Book表中找到图书名称。

【例】查询借书价格在20-40之间的读者的姓名

select name from reader 
where reader_id in(select reader_id from borrow 
	where book_id in( select book_id from book 
	where price between 20 and 40))

查询张三 ‘借阅’ 计算机文化基础’的日期

select borrowdate from borrow 
where reader_id in(
	select  reader_id from reader 
	where name='张三')
	and 
	book_id in(
	select book_id from book 
	where name='计算机文化基础')

查询借书价格在20-40之间的读者的姓名

select name from reader where reader_id in(
	select reader_id from borrow 
    where book_id in(
    select book_id from book 
    where price between 20 and 40))

SOME(子查询)

表示子查询的结果集合中某一个元素

【例】查询除不是最低价格外的所有图书

SELECT * FROM Book 
WHERE price>SOME(SELECT price FROM Book)

【例】查询价格最低的图书信息

SELECT * FROM Book 
WHERE NOT(price>SOME(SELECT price FROM Book))

ALL原理

SELECT *
FROM student
WHERE age > 18 and age > 28 and age > 38;

SELECT *
FROM student
WHERE age > ALL (
    SELECT age 
    FROM student 
    WHERE class_id = '1' -- 假设class_id为1表示“第一期”
);

all 底层:多个and比较
any底层:多个or或者比较

ALL(子查询)

表示子查询的全部结果

语法格式:

select...from 表名 where 字段 > all 查询语句

【例】查询年龄大于第一期所有年龄的学生信息

SELECT *
FROM student s
WHERE age > ALL (
    SELECT age
    FROM student
    WHERE class_id = '1' -- 假设class_id为1代表“第一期”
);

【例】查询不属于任何已存在班级的学生的所有信息

SELECT *
FROM student
WHERE class_id !=all (
    SELECT id 
    FROM class 
);

【例】查询书价最高的图书的信息

SELECT * FROM Book 
WHERE price >=ALL(SELECT price FROM Book)

【例】查询评价书价最高的出版社名称

SELECT * FROM Book 
GROUP BY public
HAVING AVG(price)>=ALL(
	SELECT AVG(price) FROM Book
	GROUP BY public)

any

select ...
    from 表名 where 字段 > all(查询语句)
相当于
select ...
    from 表名 where 字段 > result1 or 字段 > result2
SELECT * FROM student 
WHERE age > ANY (
    SELECT age 
    FROM student 
    WHERE class_id = '1'
) and class_id != 1

-- 底层将该SQL语句变成

SELECT * FROM student
WHERE (age > 18 OR age > 27 OR age > 12)
  AND class_id != 1

子查询关键字 NOT IN 和 IN

假设有一个图书馆数据库,其中包含两个表:
books(书籍)和authors(作者)。books表包含字段idtitleauthor_id等,而authors表包含字段idname

使用 IN 子查询

如果你想要查询所有由特定几位作者撰写的书籍信息,比如作者名字为“张三”或“李四”的书籍,你可以这样写:

SELECT * FROM books
WHERE author_id IN (
    SELECT id 
    FROM authors 
    WHERE name = '张三' OR name = '李四'
);

这段SQL语句会先执行子查询,找出名为“张三”或“李四”的作者的id,然后在主查询中使用这些id来筛选出对应的书籍。

使用 NOT IN 子查询

相反地,如果你想找到不是由这两位作者撰写的书籍,可以使用NOT IN

SELECT * FROM books
WHERE author_id NOT IN (
    SELECT id 
    FROM authors 
    WHERE name = '张三' OR name = '李四'
);

这个查询将会返回那些author_id不在子查询结果中的书籍记录,即排除了“张三”和“李四”所著的所有书籍。

【注意事项】

  • 在使用NOT IN时,要特别注意子查询的结果集中不能有NULL值,否则会导致整个查询结果为空。如果存在这种可能性,应该在子查询中明确过滤掉NULL值。
  • 优化器可能会根据数据库的不同对INNOT IN子查询进行不同的优化处理,有时候可能需要将它们重写为JOIN操作以获得更好的性能。

EXISTS子查询

EXISTS用于判断子查询是否存在结果,该子查询实际上并不返回任何数据,而是返回值TrueFalse
EXISTSin关键字运算效率高,实际开发中如果是数据量大的情况下推荐使用EXISTS关键字。
语法:

select * from 表名 where EXISTS
(查询语句)

先外层查询,后内层查询;
将外层的值代入内层进行查询,根据内层查询是否存在结果,判断外层的元组是否保留在结果集中。

【例】查询借阅了图书的读者的姓名

SELECT name FROM reader WHERE 
EXISTS ( SELECT * FROM borrow WHERE     
borrow.reader_id = reader.reader_id)

【例】查询被借出的图书的信息

SELECT * FROM Book 
WHERE EXISTS(SELECT * FROM Borrow WHERE Borrow.book_ID = Book.book_ID)

查询学生年龄大于18岁学生信息

SELECT * FROM students s1
WHERE EXISTS (
    SELECT * FROM student s2 
    WHERE s1.age > 18
);

查询班级下有学生的班级

SELECT * FROM class a
WHERE EXISTS (
    SELECT * FROM student b 
    WHERE b.class_id = a.id
);

查询有班级的学生信息

SELECT * FROM student s1
WHERE EXISTS (
    SELECT * FROM class c1 
    WHERE s1.class_id = c1.id
);

子查询之自关联查询

自关联查询是一种特殊的SQL查询,它允许你将同一张表视为两张或更多的虚拟表进行比较和操作(即一张表自己和自己关联,一张表当成多张表来用)。这种技术在处理具有层次结构的数据时特别有用,比如员工表中的上下级关系、分类表中的父子类别等。

注意关联查询时必须给表取别名

select 字段列表 from 表名称 a, 表名称 b where 条件;
或者
select 字段列表 from 表名称 a left join  表名称 b on 条件;

【示例场景】

假设有一个名为employees的表,该表存储了公司所有员工的信息,包括他们的ID、姓名以及直接上级的ID(manager_id)。我们希望查询每个员工及其直接上级的名字。

在这里插入图片描述

使用逗号分隔的自关联查询

你可以通过给同一个表取不同的别名来实现自关联查询:

SELECT 
    e1.id AS employee_id, 
    e1.name AS employee_name, 
    e2.id AS manager_id, 
    e2.name AS manager_name
FROM 
    employees e1, 
    employees e2
WHERE 
    e1.manager_id = e2.id;

在这个查询中,e1代表员工,而e2代表这些员工的直接上级(manager)。

使用LEFT JOIN的自关联查询

同样地,你也可以使用LEFT JOIN来进行自关联查询,这对于那些没有上级(即manager_idNULL)的员工也很有用:

SELECT 
    e1.id AS employee_id, 
    e1.name AS employee_name, 
    e2.id AS manager_id, 
    e2.name AS manager_name
FROM 
    employees e1
LEFT JOIN 
    employees e2 ON e1.manager_id = e2.id;

这个查询会列出所有员工及其对应的直接上级。如果某个员工没有上级(例如,他是公司的CEO),那么他的manager_idmanager_name将会是NULL

这两种方法都可以用来实现表自身的关联查询,选择哪一种主要取决于你的具体需求以及你想要如何处理那些不匹配的情况(比如上述例子中的顶级经理)。使用LEFT JOIN通常更灵活,因为它允许你保留左表中的所有记录,即使它们在右表中没有匹配项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二哈喇子!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值