1. 数据通常以数据库用户所需的最低层级的粒度存储
2. 分组
2.1. 隐式分组
2.1.1. mysql
-> SELECT MAX(amount) max_amt,
-> MIN(amount) min_amt,
-> AVG(amount) avg_amt,
-> SUM(amount) tot_amt,
-> COUNT(*) num_payments
-> FROM payment;
2.1.1.1. 查询返回的每个值都是由聚合函数生成的
2.1.1.2. 没有使用group by子句
2.1.1.3. 只有一个隐式分组
2.1.1.3.1. payment数据表中的所有行
2.2. 显式分组
2.2.1. mysql
-> SELECT customer_id,
-> MAX(amount) max_amt,
-> MIN(amount) min_amt,
-> AVG(amount) avg_amt,
-> SUM(amount) tot_amt,
-> COUNT(*) num_payments
-> FROM payment
-> GROUP BY customer_id;
2.2.1.1. 添加一个group by子句来指定聚合函数应该应用于哪个分组
2.3. 单列分组
2.3.1. 最简单,也是最常用的分组类型
2.3.2. mysql
-> SELECT actor_id, count(*)
-> FROM film_actor
-> GROUP BY actor_id;
2.4. 多列分组
2.4.1. 需要跨越多列生成分组
2.4.2. mysql
-> SELECT fa.actor_id, f.rating, count(*)
-> FROM film_actor fa
-> INNER JOIN film f
-> ON fa.film_id = f.film_id
-> GROUP BY fa.actor_id, f.rating
-> ORDER BY 1,2;
2.5. 通过表达式分组
2.5.1. 根据表达式产生的值构建分组
2.5.2. mysql
-> SELECT extract(YEAR FROM rental_date) year,
-> COUNT(*) how_many
-> FROM rental
-> GROUP BY extract(YEAR FROM rental_date);
2.6. 分组过滤条件
2.6.1. 由于group by子句是在where子句被评估之后运行的,因此无法为此对where子句增加过滤条件
2.6.2. 无法在where子句中引用聚合函数count(*)
2.6.2.1. 因为在评估where子句时,分组尚未生成,因而必须将分组过滤条件放入having子句
2.6.3. 向包含group by子句的查询中添加过滤条件时,仔细考虑是过滤原始数据(将过滤条件放入where子句),还是过滤分组后的数据(将过滤条件放入having子句)
3. 聚合函数
3.1. 对分组中的所有行执行特定的操作
3.2. max()
3.2.1. 返回集合中的最大值
3.3. min()
3.3.1. 返回集合中的最小值
3.4. avg()
3.4.1. 返回集合中的平均值
3.5. sum()
3.5.1. 返回集合中所有值之和
3.6. count()
3.6.1. 返回集合中所有值的个数
4. 统计不同的值
4.1. mysql
-> SELECT COUNT(customer_id) num_rows,
-> COUNT(DISTINCT customer_id) num_customers
-> FROM payment;
4.2. 通过指定distinct,count()函数检查分组中每个成员的列值,以便查找和删除重复项,而不是简单地计算分组中值的数量
4.3. mysql
-> SELECT COUNT(*) num_rows,
-> COUNT(val) num_vals,
-> SUM(val) total,
-> MAX(val) max_val,
-> AVG(val) avg_val
-> FROM number_tbl;
4.4. count(*)统计行数
4.5. count(val)统计val列包含多少个值并且忽略所有遇到的null值
5. 使用表达式
5.1. mysql
-> SELECT MAX(datediff(return_date,rental_date))
-> FROM rental;
5.2. 除了使用列作为聚合函数的参数,也可以使用表达式
6. 生成汇总
6.1. 假设在计算每位演员/评级组合的总计数的同时,还想知道不同演员参演的电影总数,这时可以运行一个额外的查询并合并结果
6.2. with rollup选项来让数据库服务器完成这些工作
6.3. mysql
-> SELECT fa.actor_id, f.rating, count(*)
-> FROM film_actor fa
-> INNER JOIN film f
-> ON fa.film_id = f.film_id
-> GROUP BY fa.actor_id, f.rating WITH ROLLUP
-> ORDER BY 1,2;
6.4. Oracle Database
6.4.1. GROUP BY ROLLUP(fa.actor_id, f.rating)
6.4.2. 可以在group_by子句中对部分列汇总
6.4.3. 如果按照列a、b、c进行分组,可以指示服务器通过下列语句仅对列b和c执行汇总
6.4.3.1. GROUP BY a, ROLLUP(b, c)
6.5. with cube选项
6.5.1. 为分组列的所有可能的组合生成汇总行
6.5.2. MySQL 8.0版并未提供
6.5.3. SQL Server和Oracle Database中可以使
7. 子查询
7.1. 子查询总是被包围在括号中,通常先于包含语句执行
7.2. 子查询像是一个具有语句作用域的临时数据表(这意味着服务器在执行SQL语句后会清空分配给子查询结果的内存)
7.3. 如果不清楚子查询究竟做了什么,可以单独运行子查询(不加括号)并查看返回结果
7.4. 返回的结果集
7.4.1. 单行单列
7.4.2. 多行单列
7.4.2.1. in和not in运算符
7.4.2.1.1. 虽然不能把单个值与一组值进行相等比较,但是可以检查这个值能否包含在一组值中
7.4.2.1.2. mysql
-> SELECT country_id
-> FROM country
-> WHERE country IN ('Canada','Mexico');
7.4.2.1.3. mysql
-> SELECT city_id, city
-> FROM city
-> WHERE country_id IN
-> (SELECT country_id
-> FROM country
-> WHERE country IN ('Canada','Mexico'));
7.4.2.1.4. sql
SELECT first_name, last_name
FROM customer
WHERE customer_id NOT IN
(SELECT customer_id
FROM payment
WHERE amount = 0)
7.4.2.1.4.1. not in的版本更易于理解
7.4.2.2. all运算符
7.4.2.2.1. 将某个值与集合中的所有值进行比较
7.4.2.2.2. mysql
-> SELECT first_name, last_name
-> FROM customer
-> WHERE customer_id <> ALL
-> (SELECT customer_id
-> FROM payment
-> WHERE amount = 0);
7.4.2.3. any运算符
7.4.2.3.1. 允许将单个值与一组值中的各个值进行比较
7.4.2.3.2. 只要有一次比较成立,使用any运算符的条件即为真
7.4.2.3.3. any与in等效
7.4.2.4. 使用not in或<>运算符比较一个值和一组值时,必须确保这组值中不包含null值,这是因为服务器会将表达式左侧的值与组中的各个值进行比较,任何值与null作相等比较时都会产生unknown
7.4.3. 多行多列
7.4.3.1. mysql
-> SELECT fa.actor_id, fa.film_id
-> FROM film_actor fa
-> WHERE fa.actor_id IN
-> (SELECT actor_id FROM actor WHERE last_name = 'MONROE')
-> AND fa.film_id IN
-> (SELECT film_id FROM film WHERE rating = 'PG');
7.4.3.2. 可以将两个单列子查询合并成一个多列子查询
7.4.3.3. mysql
-> SELECT actor_id, film_id
-> FROM film_actor
-> WHERE (actor_id, film_id) IN
-> (SELECT a.actor_id, f.film_id
-> FROM actor a
-> CROSS JOIN film f
-> WHERE a.last_name = 'MONROE'
-> AND f.rating = 'PG');