学习前提

理解SELECT、筛选和NULL;本课引入分组粒度。

本课完成后你能做什么

区分行筛选与组筛选,解释NULL对统计的影响。

先理解机制

GROUP BY将相同键的记录分为一组,COUNT与SUM再对每组计算。WHERE先筛选原始行,HAVING筛选计算后的组,两者位置不同。

COUNT(*)统计行数,COUNT(minutes)只统计非NULL分钟数。SUM与AVG忽略NULL;所有值都未知与总和为0不是同一回事。

结果中应只选择分组键或聚合表达式。SQLite允许某些未分组裸列,但它们通常不能表达你想要的确定记录;为可移植且清楚的查询避免这种写法。

可运行示例

本系列使用SQLite 3方言。打开一个全新的内存练习数据库,例如终端执行 sqlite3 :memory:,再执行本课整段SQL。每章都自带建表和样例数据,不连接网站业务数据库。

CREATE TABLE sessions(course TEXT, minutes INTEGER);
INSERT INTO sessions VALUES('Python',25),('Python',35),('SQL',20),('SQL',NULL),('Java',10);
SELECT course, COUNT(*) AS rows_count, COUNT(minutes) AS measured,
       SUM(minutes) AS total
FROM sessions GROUP BY course HAVING SUM(minutes)>=20
ORDER BY total DESC, course;

推演与验证

Python有2行、2条已测量数据、总计60;SQL有2行但只有1条已测量数据,总计20。Java总计10,被HAVING排除。

常见错误与边界

COUNT(DISTINCT course)统计不同课程数,不能代替每门课程的学习次数。聚合粒度必须先明确。

动手练习

增加AVG(minutes),推演SQL平均值是10还是20。

参考解法与理由

结果为20,因为NULL未参与分母。若产品要求缺失值按0计算,应明确写AVG(COALESCE(minutes,0)),此时是10,但含义已经改变。

进一步阅读