GROUP BY 与 HAVING
GROUP BY 把数据按指定列分组,配合聚合函数(SUM、COUNT、AVG 等)计算每组的统计值。
HAVING 对分组后的结果做过滤,和 WHERE 的区别是:WHERE 在分组前过滤行,HAVING 在分组后过滤组。
SELECT product_id, SUM(amount) AS total_sales
FROM sales
GROUP BY product_id
HAVING total_sales > 1000; SQL 执行顺序
理解执行顺序能帮你判断"这个条件该写在 WHERE 还是 HAVING":
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 所以 WHERE 里不能用聚合函数(还没分组),HAVING 里可以。SELECT 里的别名在 HAVING 中能否使用取决于数据库——MySQL 允许,但标准 SQL 不允许。
实战:每月销量冠军
典型的分组聚合 + 窗口函数组合拳:
WITH monthly_sales AS (
SELECT product_id,
DATE_FORMAT(sale_date, '%Y-%m') AS month,
SUM(amount) AS total_sales
FROM sales
GROUP BY product_id, month
),
ranked AS (
SELECT *,
RANK() OVER(PARTITION BY month ORDER BY total_sales DESC) AS rnk
FROM monthly_sales
)
SELECT product_id, month, total_sales
FROM ranked
WHERE rnk = 1; 先用 GROUP BY 聚合每产品每月销量,再用 RANK() 排名取第一。这种 CTE + 窗口函数的套路在面试中出现频率非常高。
练习
练习
消费额 Top 3 用户
给定
orders 表,找出总消费金额最高的前 3 个用户及其总金额。点击「运行」查看查询结果
查看答案
参考答案
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
ORDER BY total DESC
LIMIT 3; 解析
简单的 GROUP BY + ORDER BY + LIMIT。注意如果题目要求「并列也要保留」,就不能用 LIMIT,得用 DENSE_RANK()。
练习
连续两月达标产品
给定
sales 表,找出连续两个月销量都超过 500 的产品。提示:先按产品+月分组求总量,再用 LAG 取上月销量,两个月都 > 500 的就是目标
点击「运行」查看查询结果
查看答案
参考答案
WITH monthly AS (
SELECT product_id,
DATE_FORMAT(sale_date, '%Y-%m') AS month,
SUM(amount) AS total
FROM sales
GROUP BY product_id, DATE_FORMAT(sale_date, '%Y-%m')
),
with_prev AS (
SELECT *,
LAG(total) OVER(PARTITION BY product_id ORDER BY month) AS prev_total,
LAG(month) OVER(PARTITION BY product_id ORDER BY month) AS prev_month
FROM monthly
)
SELECT DISTINCT product_id
FROM with_prev
WHERE total > 500 AND prev_total > 500; 解析
先按月聚合,DATE_FORMAT(sale_date, '%Y-%m') 提取年月。再用 LAG 取上月销量,当月和上月都超过 500 就满足条件。如果要严格判断「连续」(月份之间没有跳月),还需要校验 prev_month 和当前 month 确实相邻。