news 2026/8/18 12:17:12

零基础学SQL 05:去重分页总搞混?DISTINCT和LIMIT一图搞懂

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零基础学SQL 05:去重分页总搞混?DISTINCT和LIMIT一图搞懂

先说点真话

做数据分析项目那会儿,有一次要从一张订单表里统计"一共有多少个客户下过单"。新人直接写了SELECT COUNT(客户id) FROM 订单表,结果数字比实际客户数多了好几倍——因为同一个客户下了多笔单,被重复算了。

还有一次,产品要看"最近 10 笔已付款订单",新人写了SELECT * FROM 订单表 WHERE 状态='已付款',结果一次性返回了几万行。

这两个问题的解法就两个字:去重(DISTINCT)和分页(LIMIT)。看着简单,但组合起来坑不少。今天一次讲透。

💡 「零基础学SQL」系列持续更新中,关注我不迷路,每篇都带标准练习数据,复制就能跑。


一、DISTINCT:去掉重复行

你会什么场景下用到它?

  • 统计"公司有几个部门"——但员工表里部门名是重复的
  • 看"有哪些客户下过单"——但订单表里同一个客户出现多次
  • 检查"有没有重复数据"——先去重看看差多少

基本写法:

-- 意图:看看员工表里到底有几个部门SELECTDISTINCT部门FROM员工表;

结果:

部门
技术部
销售部

不加 DISTINCT 的话,4 个员工会查出 4 行部门(技术部、技术部、销售部、销售部),加了就只留不重复的。

多列去重:

-- 意图:看看"每个部门有哪些不同工资档位"SELECTDISTINCT部门,工资FROM员工表;

结果:

部门工资
技术部9000.00
销售部9200.00

注意:DISTINCT 是对整行去重,不是只对第一列。(技术部, 9000)(销售部, 9200)是两行不同的数据,都保留。

坑 1:DISTINCT 和 COUNT 搭配

想统计"有几个部门",很多人会写成两步:先 DISTINCT 查出来,再数行数。其实一步搞定:

-- 对:COUNT 里直接嵌 DISTINCTSELECTCOUNT(DISTINCT部门)AS部门数FROM员工表;-- 结果:2
-- 错:这样写等于先 COUNT 再去重,语法不报错但逻辑完全不对SELECTDISTINCTCOUNT(部门)FROM员工表;-- 结果:4(先数了 4 行,再去重还是 4,毫无意义)

记住:COUNT(DISTINCT 列)才是"去重后计数",DISTINCT COUNT(列)是无意义的写法。

坑 2:DISTINCT 对 NULL 的处理

-- 意图:看看任务表里备注字段有几种不同的值SELECTDISTINCT备注FROM任务表;

结果:

备注
完成需求评审
修复线上bug
NULL

DISTINCT 会把所有 NULL 当作同一个值,只保留一个 NULL 行。如果你不想让 NULL 出现在结果里,加个 WHERE 过滤:

SELECTDISTINCT备注FROM任务表WHERE备注ISNOTNULL;

二、LIMIT:限制返回行数(分页)

你会什么场景下用到它?

  • “只看前 3 条”——快速预览数据
  • “分页显示,每页 2 条,看第 2 页”——列表展示
  • “取工资最高的 2 个人”——和 ORDER BY 搭配

基本写法:

-- 意图:只看前 2 条员工数据SELECT姓名,部门,工资FROM员工表LIMIT2;

结果:

姓名部门工资
张三技术部9000.00
李四技术部9000.00

分页写法(LIMIT + OFFSET):

-- 意图:每页 2 条,看第 2 页(跳过前 2 条)SELECT姓名,部门,工资FROM员工表LIMIT2OFFSET2;

结果:

姓名部门工资
王五销售部9200.00
赵六销售部9200.00

公式:LIMIT 每页条数 OFFSET (页码-1) × 每页条数

第 1 页:LIMIT 2 OFFSET 0(OFFSET 0 可以省略)
第 2 页:LIMIT 2 OFFSET 2
第 3 页:LIMIT 2 OFFSET 4

坑 3:LIMIT 不加 ORDER BY = 每次结果可能不一样

-- 危险写法:不加 ORDER BY,数据库不保证返回顺序SELECT姓名,工资FROM员工表LIMIT2;-- 这次可能是张三李四,下次可能是王五赵六
-- 安全写法:先排序再取前 NSELECT姓名,工资FROM员工表ORDERBY工资DESCLIMIT2;-- 结果一定是工资最高的 2 个人
姓名工资
王五9200.00
赵六9200.00

记住:LIMIT 必须和 ORDER BY 搭配才有稳定结果。不加 ORDER BY 的 LIMIT,结果顺序是"数据库心情决定的",每次可能不一样。


三、DISTINCT + LIMIT 组合:去重后取前 N

场景:想看"工资从高到低排,每个部门取第一个人"——这个需要窗口函数,后面单独讲。但"去重后取前 N 条"是可以的:

-- 意图:去重后的部门,只取第 1 个SELECTDISTINCT部门FROM员工表ORDERBY部门LIMIT1;-- 结果:技术部

执行顺序:FROM → DISTINCT → ORDER BY → LIMIT。先去重,再排序,最后取前 N 条。


四、速查卡(建议收藏)

操作写法典型场景
去重查列SELECT DISTINCT 列 FROM 表看有哪些不同的值
去重计数SELECT COUNT(DISTINCT 列) FROM 表统计不重复的值有多少个
多列去重SELECT DISTINCT 列1, 列2 FROM 表看组合不重复的行
取前N条SELECT ... FROM 表 LIMIT N预览/取Top N
分页SELECT ... FROM 表 LIMIT N OFFSET M列表分页展示
排序后取前NSELECT ... FROM 表 ORDER BY 列 LIMIT N取最高/最低的N条

三条铁律:

  1. COUNT(DISTINCT 列)才是去重计数,不要写DISTINCT COUNT(列)
  2. LIMIT 必须配 ORDER BY,不然结果顺序不稳定
  3. DISTINCT 把多个 NULL 当作同一个值,不想看 NULL 就加WHERE 列 IS NOT NULL

五、练习题(先自己写,再看答案)

题目 1:统计订单表里有多少个不同的员工下过单。

题目 2:查询订单表里金额最高的 2 笔订单,显示订单id、订单金额、状态。

题目 3:查询任务表里备注字段有多少种不同的非空值。

题目 4:分页查询员工表,每页 2 条,按入职日期从早到晚排序,写出第 2 页的 SQL。


参考答案

题目 1:

SELECTCOUNT(DISTINCT员工id)AS下单员工数FROM订单表;-- 结果:3(员工1、员工2、员工3,员工4没下过单)

题目 2:

SELECT订单id,订单金额,状态FROM订单表ORDERBY订单金额DESCLIMIT2;-- 结果:103(4000)、101(3000)

题目 3:

SELECTCOUNT(DISTINCT备注)AS不同备注数FROM任务表WHERE备注ISNOTNULL;-- 结果:2("完成需求评审"和"修复线上bug",NULL被排除)

题目 4:

SELECT姓名,部门,入职日期FROM员工表ORDERBY入职日期LIMIT2OFFSET2;-- 结果:李四(2020-07-15)、张三(2019-03-01)... 按日期排:王五(2018-01-10)、张三(2019-03-01)是第1页,李四(2020-07-15)、赵六(2021-05-20)是第2页

你站哪队?

学 SQL 的时候,你是"先把所有语法过一遍再动手"型,还是"边写边查、踩坑了再记"型?

我当年是后者——踩了无数坑才记住COUNT(DISTINCT 列)DISTINCT COUNT(列)的区别。评论区聊聊你学 SQL 的方式,我看看哪种人多。


从零数据分析· 十年数据分析经验 · 实战笔记

SQL 从入门到精通 28 篇持续更新中,每篇附标准练习数据,复制即运行。

关注我,把 SQL 学成肌肉记忆。

📋 系列目录:零基础学SQL:从入门到精通完整目录(28篇持续更新)
📖 上篇:篇04 WHERE条件总写错?AND/OR/IN一图搞懂
📖 下篇预告:篇06 数据增删改——INSERT/UPDATE/DELETE 的安全写法(别把生产库删了)


附录:标准练习数据(复制即可运行,全系列通用)

-- 全系列通用四张表,每次运行先 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,'待分配');
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/18 12:13:26

B站视频下载新思路:把大会员4K和充电专属内容装进你的硬盘

B站视频下载新思路:把大会员4K和充电专属内容装进你的硬盘 【免费下载链接】bilibili-downloader B站视频下载,支持下载大会员清晰度4K,持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 你是不是也有过…

作者头像 李华
网站建设 2026/8/18 12:13:19

斯柯达中国市场韧性解读:4.9%增长背后的差异化生存法则

1. 一个“非主流”的逆袭:斯柯达中国市场的韧性解读 当大家的目光都聚焦在新能源、智能驾驶和头部品牌的价格战时,一个来自捷克的百年品牌,斯柯达,在中国市场交出了一份看似“低调”却值得玩味的成绩单:年销量超过34.1…

作者头像 李华
网站建设 2026/8/18 12:12:55

【教程】DE25-Nano开发板 Wifi Dongle测试演示

友晶科技官网发布的DE25-Nano开发板Linux镜像de25_nano_revA_sdcard_console_v1.1.img支持WiFi功能。本文将演示DE25-Nano开发板 Wifi Dongle测试全过程。 通过一个Type C 接口的USB Hub模块给DE25-Nano开发板连接上USB WiFi模块(友晶官网有售)&#xf…

作者头像 李华
网站建设 2026/8/18 12:12:20

大模型应用优化:以简化设计提升智能体、推理与代码生成的可靠性

1. 项目概述:当“更少”成为智能体、推理与编码大模型的“更多” 最近在折腾大模型应用落地的朋友,估计都听过一个词: Agentic 。它描绘的是一种能自主感知、规划、决策和执行的智能体形态,是让LLM从“聊天机器”走向“实干家”…

作者头像 李华