SQL 入门:筛选有效记录并按标签分组统计
创建小型学习记录表,使用 WHERE 排除无效数据,再通过 GROUP BY 与 HAVING 挑出总时长达标的标签。
本节目标
创建小型学习记录表,使用 WHERE 排除无效数据,再通过 GROUP BY 与 HAVING 挑出总时长达标的标签。
环境与运行方法
使用 SQLite 3 验证本组 SQL。将完整示例保存为 lesson.sql,执行 sqlite3 :memory: < lesson.sql。每次运行都是独立内存数据库,不会覆盖已有数据;其他数据库的类型和事务细节可能不同。
核心思路
WHERE 作用于分组之前的行,HAVING 作用于分组后的结果。先明确负值无效,再按标签累计。NULL 表示缺失,SQL 的比较逻辑与普通二值布尔逻辑不同,应显式处理。
完整示例
CREATE TABLE records(tag TEXT NOT NULL, minutes INTEGER);
INSERT INTO records VALUES
('sql',20),('sql',15),('cli',10),('cli',-5),('missing',NULL);
SELECT tag, COUNT(*) AS valid_count, SUM(minutes) AS total
FROM records
WHERE minutes IS NOT NULL AND minutes >= 0
GROUP BY tag
HAVING SUM(minutes) >= 30
ORDER BY tag;预期输出
sql|2|35逐步理解
过滤后 cli 只有 10 分钟,不满足分组门槛;sql 两条合计 35,通过 HAVING。ORDER BY 保证输出顺序,不依赖存储或执行计划。
COUNT(*) 统计当前分组中的行,COUNT(minutes) 忽略该字段为 NULL 的行。此查询已先排除 NULL,因此两者数值相同,但换一个查询不一定相同。理解过滤发生的阶段,比记住某次结果更重要。
常见问题与边界
- 不能在 WHERE 中直接把聚合条件当作行条件。
- 不写 ORDER BY 不能保证顺序。
- NULL 不应使用等号比较。
动手练习
将 cli 的 10 改成 30,再让 sql 的 15 变成 NULL。
参考答案
cli 合计 30 会输出,sql 只剩 20 不再达标。HAVING 检查的是过滤后的聚合结果,不是原表里曾经出现过的总量。
文章回复
0 条公开回复