汇总、分组数据:Group By、Having、Where
GROUP BY 和 HAVING 子句的用法:
-
GROUP BY用于将结果集按照指定的列进行分组。 -
HAVING用于对分组后的结果进行筛选。
HAVING和Where 区别:
- 执行顺序差异
在 SQL 查询中,各子句有特定的执行顺序,WHERE 子句在分组操作(GROUP BY)之前执行,而 HAVING 子句在分组操作之后执行。
-
WHERE子句:在分组之前筛选出符合条件的原始行,减少参与分组的数据量,从而提高性能。 -
HAVING子句:在分组之后对分组结果进行筛选,仅保留符合特定条件的分组。 -
操作对象不同
-
WHERE子句:只能对原始表中的列进行条件筛选,不能使用聚合函数。因为在WHERE子句执行时,分组和聚合操作尚未进行,聚合函数的值还未计算出来。 -
HAVING子句:可以使用聚合函数,因为它是在分组和聚合操作完成后执行的,能够对分组后的结果进行筛选。
子查询
子查询的定义
子查询是嵌套在其他 SQL 语句中的查询,也就是我们先需要从表中,查询&筛选出一个虚拟数据集,再根据这个集合做一些操作。
子查询与 IN 操作符结合使用
- 结合方式:
IN操作符可和子查询搭配,用来判断某个值是否存在于子查询的结果集中。 - 示例及处理流程:查询语句
SELECT * FROM language WHERE language_id IN
(SELECT DISTINCT language_id FROM film);
中,MySQL 先执行子查询 SELECT DISTINCT language_id FROM film 得到结果集,然后依据此结果集执行外层查询,相当于执行 SELECT * FROM language WHERE language_id IN (具体结果值);
EXISTS 与子查询
EXISTS的作用:EXISTS是布尔运算符,用于检验子查询是否会返回行。若子查询返回至少一行,EXISTS就返回TRUE;反之则返回FALSE。- 示例说明:如
SELECT * FROM language WHERE EXISTS
(SELECT * FROM film WHERE film.language_id = language.language_id);
子查询作为 EXISTS 子句的参数,用于查询符合条件的数据行。
派生表
-
派生表的定义:当子查询处于
FROM子句中时,该子查询就被叫做派生表。 -
派生表的规则:
- 派生表一定要有别名,因为 MySQL 规定
FROM子句中的表都得有名字。
- 派生表一定要有别名,因为 MySQL 规定
-
示例:在
SELECT * FROM
(SELECT last_name, COUNT(*) count FROM actor GROUP BY last_name) t
WHERE t.last_name LIKE 'A%';
中,SELECT last_name, COUNT(*) count FROM actor GROUP BY last_name 就是一个派生表,它有别名 t。
- 与临时表区别:需注意,派生表并非临时表。
总结
- 子查询是嵌套在其他 SQL 语句里的查询。
- 子查询常作为比较运算的操作数,用在
WHERE子句中。 - 位于
FROM子句中的子查询是派生表,派生表必须有别名
子查询
根据子查询是否依赖外部,分为两种子查询,独立子查询和依赖子查询。而依赖子查询会导致SQL的性能急剧下降。
依赖子查询
在mysql8.0以前,可能会被识别成依赖子查询,从而导致:
比如:
SELECT * FROM orders WHERE (o_clerk , o_orderdate) IN
( SELECT o_clerk, MAX(o_orderdate) FROM orders GROUP BY o_clerk);
这种情况下,执行顺序是:
- 外部select * from orders 先取出一行(o_clerk, o_orderdate)
- 子查询根据当前行,执行一遍
- 重复1
这就导致了O(复杂度) = O(子查询行数 * 子查询行数)。
依赖子查询优化-子查询派生表
建立子查询派生表,通过外表和派生表的join操作,降低查询复杂度从:
O(子查询行数 * 子查询行数) => O(常数*子查询行数)