学习前提
理解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,但含义已经改变。