零基础学SQL 08:查询总写一半就卡住?一张订单表串起SELECT到聚合
2026/9/23 21:43:37 网站建设 项目流程

先说点真话

前七篇你学了 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 条全出来了。但老板只要已付款的。


三、第二步:筛选(WHERE)

SELECT订单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.00
314000.00

GROUP 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 笔的员工",就需要在分组之后筛掉不满足的组——这时候用 HAVING:

SELECT员工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-L(From → Where → GroupBy → Having → Select → OrderBy → Limit)。

记住这个顺序,写查询就不会懵——先想"从哪张表" → “过滤什么行” → “要不要分组” → “过滤什么组” → “选什么列” → “怎么排序” → “要几行”。


八、踩坑:SELECT 里写了不在 GROUP BY 里的列

-- 错:订单金额不是聚合列,也不在 GROUP BY 里SELECT员工id,订单金额,COUNT(*)AS订单数FROM订单表GROUPBY员工id;

为什么报错?GROUP BY 把同一个员工的订单合并成一行,但"订单金额"既不在 GROUP BY 里、也不是聚合函数(比如 SUM)——一个员工有多笔订单,数据库不知道该取哪一笔的金额,没法合并,直接报错。

MySQL 严格模式下报1055(ER_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 和金额。


参考答案

题目 1:

SELECT订单id,员工id,订单金额,状态FROM订单表WHERE状态='已付款'ORDERBY订单金额DESC;-- 结果:103(4000) → 101(3000) → 102(2500)

题目 2:

SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态='已付款'GROUPBY员工id;-- 结果:员工1→2单5500,员工3→1单4000

题目 3:

SELECT员工id,COUNT(*)AS订单数,SUM(订单金额)AS总金额FROM订单表WHERE状态='已付款'GROUPBY员工idHAVINGSUM(订单金额)>3000;-- 结果:员工1→5500,员工3→4000(都超过3000)

题目 4:

SELECT订单id,订单金额FROM订单表WHERE状态='已付款'ORDERBY订单金额DESCLIMIT1;-- 结果:订单103,4000.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,'zhangsan@demo.com','13800000001','2019-03-01'),(2,'李四','技术部',9000.00,'lisi@demo.com','13800000002','2020-07-15'),(3,'王五','销售部',9200.00,'wangwu@demo.com','13800000003','2018-01-10'),(4,'赵六','销售部',9200.00,'zhaoliu@demo.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','zhangsan@demo.com','北京市朝阳区'),(2,'李四','13900000002','lisi@demo.com','上海市浦东新区'),(3,'王五','13900000003','wangwu@demo.com','广州市天河区');DROPTABLEIFEXISTS任务表;CREATETABLE任务表(任务idINTPRIMARYKEY,员工idINT,备注VARCHAR(100),状态VARCHAR(20));INSERTINTO任务表(任务id,员工id,备注,状态)VALUES(1,1,'完成需求评审','已完成'),(2,2,NULL,'进行中'),(3,3,'修复线上bug','已完成'),(4,4,NULL,'待分配');

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询