先说点真话前七篇你学了 SELECT、WHERE、ORDER BY、GROUP BY、聚合函数、NULL 的坑——单拆开每个都懂拼到一起就懵。我带新人的时候见过太多了给了个需求对方盯着屏幕半天不知道从哪开始写。问题不是学得不够是没练过串起来。今天用一个真实需求从最简单的 SELECT 一路写到聚合分组排序限制把篇01-07的知识点一次串完。 「零基础学SQL」系列持续更新中关注我不迷路每篇都带标准练习数据复制就能跑。一、需求来了老板说“看看每个员工的订单情况已付款的按总金额排序取第一名。”拆成 4 步挑列 → 筛选 → 分组 → 排序限制HAVING 作为扩展单独讲。每步对应你学过的一个知识点。二、第一步挑列SELECT最朴素的写法是SELECT *但生产环境别这么干——多查不用的列浪费网络传输、占内存还可能暴露敏感字段手机号、邮箱。-- 意图只取需要的列SELECT订单id,员工id,订单金额,状态FROM订单表;订单id员工id订单金额状态10113000.00已付款10212500.00已付款10334000.00已付款10421500.00待付款4 条全出来了。但老板只要已付款的。三、第二步筛选WHERESELECT订单id,员工id,订单金额,状态FROM订单表WHERE状态已付款;订单id员工id订单金额状态10113000.00已付款10212500.00已付款10334000.00已付款3 条排除了订单 104待付款。老板想看金额从高到低。四、第三步分组聚合GROUP BY COUNT/SUM需求里每个员工的订单情况就是按员工汇总。用 GROUP BY 聚合函数SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工id;员工id订单数总金额125500.00314000.00GROUP BY 员工id 把数据按员工合并——员工 1 有 2 笔已付款订单合并成 1 行员工 3 有 1 笔也是 1 行。COUNT(*) 数行数SUM(订单金额) 算总和。这就是聚合查询的核心明细变汇总多行变一行。到这步每个员工的订单情况已经出来了但还没排序、还没取第一名。五、第四步排序限制ORDER BY LIMIT需求后半句按总金额排序取第一名。在分组汇总的基础上排SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工idORDERBYSUM(订单金额)DESCLIMIT1;员工id订单数总金额125500.00员工 1 总金额 5500 最高排第一LIMIT 1 取第一条——这就是老板要的每个员工已付款订单里总金额第一名。注意 ORDER BY 用的是SUM(订单金额)而不是别名总金额——虽然 MySQL 里 ORDER BY 能用别名但写全表达式更通用换数据库也不报错。六、HAVING 是干嘛的知识扩展原需求用不上但必学上面这个需求没用到 HAVING。但如果老板补一句只要订单数至少 2 笔的员工就需要在分组之后筛掉不满足的组——这时候用 HAVINGSELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工idHAVINGCOUNT(*)2;员工id订单数总金额125500.00员工 3 只有 1 笔被 HAVING 过滤掉了。WHERE 和 HAVING 的区别WHERE 过滤行分组前HAVING 过滤组分组后。WHERE 用不了聚合函数HAVING 可以。记住这个区别遇到分组后筛选就想到 HAVING。七、完整执行顺序速查卡建议收藏上面的查询合在一起看SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额-- 5. 选列FROM订单表-- 1. 定表WHERE状态已付款-- 2. 过滤行GROUPBY员工id-- 3. 分组ORDERBYSUM(订单金额)DESC-- 4. 排序LIMIT1;-- 5. 限制SQL 的书写顺序和执行顺序不一样这是新手最大的困惑来源顺序关键字作用能用聚合函数能用别名1FROM确定从哪张表取数据否否2WHERE过滤行分组前否否3GROUP BY分组否部分4HAVING过滤组分组后是否5SELECT选列/聚合计算是—6ORDER BY排序是是7LIMIT限制行数否否上面这个需求没用到 HAVING但它排在 GROUP BY 之后、SELECT 之前是 SQL 完整执行顺序的一环记熟 F-W-G-H-S-O-L 就不会漏。口诀F-W-G-H-S-O-LFrom → Where → GroupBy → Having → Select → OrderBy → Limit。记住这个顺序写查询就不会懵——先想从哪张表 → “过滤什么行” → “要不要分组” → “过滤什么组” → “选什么列” → “怎么排序” → “要几行”。八、踩坑SELECT 里写了不在 GROUP BY 里的列-- 错订单金额不是聚合列也不在 GROUP BY 里SELECT员工id,订单金额,COUNT(*)AS订单数FROM订单表GROUPBY员工id;为什么报错GROUP BY 把同一个员工的订单合并成一行但订单金额既不在 GROUP BY 里、也不是聚合函数比如 SUM——一个员工有多笔订单数据库不知道该取哪一笔的金额没法合并直接报错。MySQL 严格模式下报1055ER_WRONG_FIELD_WITH_GROUP搜 “MySQL 1055” 或 “not in GROUP BY” 就能定位。非严格模式虽然不报错但返回的订单金额是随机一笔结果不可信——别侥幸。铁规则SELECT 里的非聚合列必须出现在 GROUP BY 里。要么把列加到 GROUP BY要么用聚合函数包起来SUM、MAX 等。-- 对订单金额用聚合函数包起来SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工id;九、练习题先自己写再看答案题目 1查出订单表中所有已付款订单按订单金额从高到低排序。题目 2按员工分组统计每个员工已付款订单的总金额和订单笔数。题目 3在题目 2 基础上只显示总金额超过 3000 的员工。题目 4查出已付款订单中金额最高的那笔订单的 id 和金额。参考答案题目 1SELECT订单id,员工id,订单金额,状态FROM订单表WHERE状态已付款ORDERBY订单金额DESC;-- 结果103(4000) → 101(3000) → 102(2500)题目 2SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工id;-- 结果员工1→2单5500员工3→1单4000题目 3SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态已付款GROUPBY员工idHAVINGSUM(订单金额)3000;-- 结果员工1→5500员工3→4000都超过3000题目 4SELECT订单id,订单金额FROM订单表WHERE状态已付款ORDERBY订单金额DESCLIMIT1;-- 结果订单1034000.00你写查询的习惯是什么你写 SQL 的时候是先写 SELECT 还是先写 FROM我带过的人里一半先写 SELECT想着要什么列一半先写 FROM想着从哪取数。我个人习惯先写 FROM 和 WHERE——先把数据范围圈出来再想选什么列。评论区聊聊你的顺序。从零数据分析· 十年数据分析经验 · 实战笔记SQL 从入门到精通 28 篇持续更新中每篇附标准练习数据复制即运行。关注我把 SQL 学成肌肉记忆。 系列目录零基础学SQL从入门到精通完整目录28篇持续更新 上篇篇07 COUNT统计总对不上聚合函数和NULL的坑一图搞懂附录标准练习数据复制即可运行全系列通用-- 全系列通用四张表每次运行先 DROP 再 CREATE不会报表已存在DROPTABLEIFEXISTS员工表;CREATETABLE员工表(员工idINTPRIMARYKEY,姓名VARCHAR(20),部门VARCHAR(20),工资DECIMAL(10,2),邮箱VARCHAR(50),手机VARCHAR(20),入职日期DATE);INSERTINTO员工表(员工id,姓名,部门,工资,邮箱,手机,入职日期)VALUES(1,张三,技术部,9000.00,zhangsandemo.com,13800000001,2019-03-01),(2,李四,技术部,9000.00,lisidemo.com,13800000002,2020-07-15),(3,王五,销售部,9200.00,wangwudemo.com,13800000003,2018-01-10),(4,赵六,销售部,9200.00,zhaoliudemo.com,13800000004,2021-05-20);DROPTABLEIFEXISTS订单表;CREATETABLE订单表(订单idINTPRIMARYKEY,员工idINT,订单金额DECIMAL(10,2),下单时间DATETIME,付款时间DATETIME,状态VARCHAR(20));INSERTINTO订单表(订单id,员工id,订单金额,下单时间,付款时间,状态)VALUES(101,1,3000.00,2026-01-10 10:00:00,2026-01-10 10:05:00,已付款),(102,1,2500.00,2026-02-15 14:00:00,2026-02-15 14:10:00,已付款),(103,3,4000.00,2026-03-20 09:30:00,2026-03-20 09:40:00,已付款),(104,2,1500.00,2026-04-05 16:00:00,NULL,待付款);DROPTABLEIFEXISTS用户表;CREATETABLE用户表(用户idINTPRIMARYKEY,姓名VARCHAR(20),手机VARCHAR(20),邮箱VARCHAR(50),地址VARCHAR(100));INSERTINTO用户表(用户id,姓名,手机,邮箱,地址)VALUES(1,张三,13900000001,zhangsandemo.com,北京市朝阳区),(2,李四,13900000002,lisidemo.com,上海市浦东新区),(3,王五,13900000003,wangwudemo.com,广州市天河区);DROPTABLEIFEXISTS任务表;CREATETABLE任务表(任务idINTPRIMARYKEY,员工idINT,备注VARCHAR(100),状态VARCHAR(20));INSERTINTO任务表(任务id,员工id,备注,状态)VALUES(1,1,完成需求评审,已完成),(2,2,NULL,进行中),(3,3,修复线上bug,已完成),(4,4,NULL,待分配);