📊 SQL 入门 Day 2:排序与去重 — 让数据有序呈现

学会排序和去重,告别杂乱无章的查询结果。

📝 知识点简介

查询数据很容易,但查出来的结果往往是无序 的。要让数据按你想要的顺序排列,或者去除重复行,就需要掌握 ORDER BY 和 DISTINCT。这两个看似简单的语法,实际使用中有不少细节坑。


一、ORDER BY --- 排序

1. 基本语法

sql 复制代码
SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC | DESC], column2 [ASC | DESC], ...;
  • ASC:升序(默认,可省略)
  • DESC:降序

2. 单列排序

sql 复制代码
-- 按薪水升序排列(ASC 可省略)
SELECT emp_name, salary
FROM employees
ORDER BY salary;

-- 按薪水降序排列
SELECT emp_name, salary
FROM employees
ORDER BY salary DESC;

3. 多列排序

先按第一列排,相同值时按第二列排:

sql 复制代码
-- 先按部门升序,再按薪水降序
SELECT dept_id, emp_name, salary
FROM employees
ORDER BY dept_id ASC, salary DESC;

执行结果示意:

diff 复制代码
dept_id | emp_name | salary
--------|----------|-------
1       | 张三     | 15000
1       | 李四     | 12000
1       | 王五     | 12000
2       | 赵六     | 18000
2       | 钱七     | 10000

4. 按表达式/别名排序

sql 复制代码
-- 按计算字段排序
SELECT emp_name, salary, salary * 1.1 AS new_salary
FROM employees
ORDER BY new_salary DESC;

-- 按函数结果排序
SELECT emp_name, hire_date
FROM employees
ORDER BY EXTRACT(YEAR FROM hire_date);

5. 按列位置排序(不推荐)

某些数据库支持按 SELECT 中的列序号排序:

sql 复制代码
SELECT emp_name, salary, hire_date
FROM employees
ORDER BY 2 DESC;  -- 等价于 ORDER BY salary DESC

⚠️ 建议:使用列名而非序号,可读性更好,且修改 SELECT 列时不会意外改变排序行为。

6. NULL 值的排序

NULL 的排序位置因数据库而异:

数据库 NULL 默认排在 控制方式
MySQL 升序排前面,降序排后面 无标准语法
PostgreSQL 升序排后面,降序排前面 NULLS FIRST / NULLS LAST
SQL Server 升序排前面,降序排前面 无标准语法
Oracle 升序排后面,降序排前面 NULLS FIRST / NULLS LAST
sql 复制代码
-- PostgreSQL / Oracle:把 NULL 排到最后
SELECT emp_name, bonus
FROM employees
ORDER BY bonus DESC NULLS LAST;

二、LIMIT / OFFSET --- 分页与限制

1. MySQL / PostgreSQL

sql 复制代码
-- 取前 5 条
SELECT * FROM employees ORDER BY salary DESC LIMIT 5;

-- 跳过前 5 条,取接下来的 5 条(第 6-10 条)
SELECT * FROM employees ORDER BY salary DESC LIMIT 5 OFFSET 5;

简写形式(MySQL, PostgreSQL):

sql 复制代码
SELECT * FROM employees ORDER BY salary DESC LIMIT 5, 5;
-- 含义:LIMIT 偏移量, 条数(⚠️ 和上面的 OFFSET 写法参数顺序相反)

2. SQL Server / PostgreSQL FETCH

sql 复制代码
-- SQL Server / PostgreSQL 标准语法
SELECT emp_name, salary
FROM employees
ORDER BY salary DESC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY;

-- 第 2 页(第 6-10 条)
OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;

3. ORDER BY 在分页中的重要性

sql 复制代码
-- ❌ 危险!无 ORDER BY 的分页结果不确定
SELECT * FROM employees LIMIT 10 OFFSET 0;
SELECT * FROM employees LIMIT 10 OFFSET 10;
-- 两页之间可能有重复/遗漏

-- ✅ 必须加 ORDER BY,且排序列要唯一
SELECT * FROM employees
ORDER BY emp_id
LIMIT 10 OFFSET 0;

SELECT * FROM employees
ORDER BY emp_id
LIMIT 10 OFFSET 10;

⚠️ 分页必知 :ORDER BY 的列如果有重复值,同一行可能出现在不同页。最佳实践 :用 id + created_at 等唯一组合排序。


三、DISTINCT --- 去重

1. 单列去重

sql 复制代码
-- 查询所有不重复的部门
SELECT DISTINCT dept_id
FROM employees;

2. 多列去重

sql 复制代码
-- 查询所有不重复的(部门, 职位)组合
SELECT DISTINCT dept_id, job_title
FROM employees;

DISTINCT 作用于所有选中的列的组合,只有当所有列的值都相同时才视为重复。

3. DISTINCT 与聚合函数

sql 复制代码
-- 所有员工数
SELECT COUNT(*) FROM employees;          -- 100 条

-- 有员工的部门数(去重后)
SELECT COUNT(DISTINCT dept_id) FROM employees;  -- 5 个

四、常见坑点与注意事项

坑 1:DISTINCT 不能部分去重

sql 复制代码
-- ❌ 语法错误:DISTINCT 只能放在 SELECT 后面第一个
SELECT emp_name, DISTINCT dept_id FROM employees;

-- ✅ 正确写法
SELECT DISTINCT dept_id, emp_name FROM employees;

坑 2:大结果集慎用 ORDER BY

sql 复制代码
-- 百万级数据全量排序非常慢
SELECT * FROM logs ORDER BY created_at DESC LIMIT 100;
-- ✅ 有 LIMIT 时数据库可能只排前 100 条(视优化器而定)

坑 3:字符串排序不是你以为的样子

sql 复制代码
-- 按数字字符串排序
SELECT id FROM tickets ORDER BY id;
-- 结果:1, 10, 100, 2, 20, 200 ...(字典序!)

-- 解决方案:类型转换
SELECT id FROM tickets ORDER BY CAST(id AS INTEGER);
-- 或补零:ORDER BY LPAD(id, 10, '0')

坑 4:DISTINCT 和 ORDER BY 的列必须匹配

sql 复制代码
-- ❌ 某些数据库报错
SELECT DISTINCT dept_id FROM employees ORDER BY salary;

-- ✅ 排序的列必须在 SELECT 中
SELECT DISTINCT dept_id, salary FROM employees ORDER BY salary;

🎯 今日总结

语法 要点
ORDER BY col ASC/DESC 默认 ASC,多列用逗号分隔
ORDER BY col DESC NULLS LAST PostgreSQL/Oracle 可用
LIMIT n OFFSET m MySQL/PostgreSQL 分页
OFFSET m FETCH NEXT n ONLY SQL 标准,SQL Server/PostgreSQL
DISTINCT 作用于所有选中列的组合
COUNT(DISTINCT col) 去重计数

一句话记住: 查结果想有序 → ORDER BY;查结果想去重 → DISTINCT;两样都想要 → 先 DISTINCT 再 ORDER BY,且排序列必须在 SELECT 中。

相关推荐
星恒随风6 小时前
MySQL 从零入门(五):MySQL 内置函数详解——日期、字符串、数学函数与实战案例
linux·数据库·笔记·sql·mysql
2603_969694859 小时前
【JavaWeb学习记录】MyBatis核心知识点与实战笔记:从入门到动态SQL
笔记·sql·学习·mysql·mybatis·javaweb·学习方法
数据技术说11 小时前
在 KES AI 数据库上搭一条 NL2SQL 链路:语义、召回、权限各放哪
人工智能·sql·代码规范
codeejun21 小时前
每日一Go·MySQL-14、慢 SQL 产生原因全解
sql·mysql·golang
新*1 天前
Elasticsearch 7.17 的 SQL 到底能查到什么?一次踩坑记录
后端·sql·elasticsearch·查询
这个DBA有点耶2 天前
库内计算推理:一条SQL背后的算子融合,和外挂方案差了几次网络往返
数据库·人工智能·sql
Decay__3 天前
01_SQLServer 体系架构全解 —— 实例进程、关系引擎、SQLOS 与事务日志深入
运维·数据库·sql·mysql·架构
瑞雪兆丰年兮3 天前
JavaWeb-day07-MySQL(DQL&多表设计)
java·开发语言·数据库·sql·mysql·javaweb·web
freejackman4 天前
【工程化】从“单兵作战”到“多引擎架构”:基于门面模式的重构实战
大数据·数据库·hive·sql·测试工具·设计模式·架构