学会排序和去重,告别杂乱无章的查询结果。
📝 知识点简介
查询数据很容易,但查出来的结果往往是无序 的。要让数据按你想要的顺序排列,或者去除重复行,就需要掌握 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 中。