首页 / PostgreSQL 入门教程 / ORDER BY 排序

PostgreSQL 入门教程

ORDER BY 排序

本教程共 50 篇 · 第 26 篇 · 更新于 2026-07-31 · 约 8 分钟阅读

PostgreSQLPostgreSQL 入门教程ORDER BYASCDESCNULLS FIRST

26. ORDER BY 排序

本节目标:学会用 ORDER BY 给查询结果排序,掌握升序降序、多列排序、按别名/表达式排序,以及控制 NULL 的位置。

SELECT 查出来的行,默认顺序是”未指定”的,可能每次都不同。想按某一列有序展示,就得用 ORDER BY

先用示例的 users 表做演示:

CREATE TABLE users (
  id        INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  username  VARCHAR(50),
  email     VARCHAR(100),
  age       INT,
  created_at DATE
);

INSERT INTO users (username, email, age, created_at) VALUES
  ('alice',  'alice@example.com',  28, '2025-01-12'),
  ('bob',    'bob@example.com',    35, '2025-03-08'),
  ('charlie','charlie@example.com',19, '2025-05-20'),
  ('dave',   'dave@example.com',   42, '2025-07-01'),
  ('emma',   NULL,                 31, '2025-09-15');

升序 ASC 与降序 DESC

ORDER BY 列名 默认按升序(从小到大)排。显式写 ASC 效果一样;要倒过来就写 DESC

-- 按年龄从大到小排
SELECT username, age
FROM users
ORDER BY age DESC;
Note

文本类型按字符集顺序排,数字和日期按大小排。ASC 是默认值,不写也行。

多列排序

ORDER BY 后面可以跟多列,用逗号分开。排序时先按第一列排,第一列相同的行,再按第二列排,依此类推。

-- 先按年龄升序,年龄相同再按用户名降序
SELECT username, age
FROM users
ORDER BY age ASC, username DESC;

注意顺序很重要:把哪一列放前面,它就主导排序。放后面的列只在”前面列值相等”时才起作用。比如两个人同龄时,用户名才会决定谁在前。

按别名排序

SELECT 里起的列别名(alias),可以直接在 ORDER BY 里用。这是因为 PostgreSQL 先算 SELECT,再排 ORDER BY,别名那时已经存在了。

-- 给年龄起别名,再按别名排
SELECT username, age AS user_age
FROM users
ORDER BY user_age DESC;

按表达式排序

排序依据不限于某一列,也可以是一个表达式。比如按用户名的长度排:

-- LENGTH() 返回字符串长度,按长度从长到短排
SELECT username, LENGTH(username) AS name_len
FROM users
ORDER BY name_len DESC;
Tip

表达式排序很灵活,但也意味着数据库要对每行算一遍表达式。列多、数据大时留意性能,必要时刻考虑冗余存一列或建函数索引。

NULLS FIRST / LAST 控制空值位置

NULL 表示”没有值”。排序时,空值排前面还是后面,可以用 NULLS FIRSTNULLS LAST 明确指定。

-- 把 email 为空的行排到最后
SELECT username, email
FROM users
ORDER BY email NULLS LAST;

默认规则要记一下:升序(ASC)时空值在最后(NULLS LAST),降序(DESC)时空值在最前(NULLS FIRST)。如果你想要反过来的效果,就显式写出来:

-- 降序但让空值留在最后
SELECT username, email
FROM users
ORDER BY email DESC NULLS LAST;
Warning

不同数据库对 NULL 的默认排序位置可能不同。写跨库 SQL 时,建议显式带上 NULLS FIRST/LAST,避免行为不一致。

用列位置数字排序

ORDER BY 后面除了写列名,还能写数字,代表 SELECT 里第几列(从 1 开始)。写起来快,但可读性差:

-- 等价于 ORDER BY username, age
SELECT username, age
FROM users
ORDER BY 1, 2;
Warning

用数字排序很容易踩坑:一旦调整了 SELECT 的列顺序,ORDER BY 的数字没跟着改,排序就偷偷变了。生产代码建议老老实实写列名。

随机打乱顺序

想让结果随机排序,可以用 RANDOM() 函数(返回 0~1 的随机数)。常配合 LIMIT 做”随机抽几条”:

-- 随机抽 2 个用户
SELECT username
FROM users
ORDER BY RANDOM()
LIMIT 2;
Note

RANDOM() 排序要对全表打乱,数据量大时很慢,别在频繁查询里用。它和 LIMIT 搭配做”随机取样”很顺手。

常见错误

  • ORDER BY 写在 WHERE 前面会报语法错,顺序必须是 WHERE 先、ORDER BY 后。
  • 想”先按某列升序、再按某列降序”,却两列都写成 DESC,导致顺序不符合预期。
  • 文本排序依赖数据库的字符集(collation),中文按拼音还是按笔画由排序规则决定,换环境可能不同,需要稳定顺序时显式指定。
  • 用数字位置排序后改了 SELECT 列,却忘了改数字,排序结果静默出错。

ORDER BY + LIMIT 取”前 N 名”

排序最常见的搭配就是取榜单前几名:

-- 年龄最大的 3 位用户
SELECT username, age
FROM users
ORDER BY age DESC
LIMIT 3;

如果第 3 名有并列,LIMIT 3 会随机丢几个。想要”并列都保留”,用上一章讲的 FETCH FIRST ... WITH TIES,或者把打破平局的列也写进 ORDER BY,让顺序唯一。

每列单独指定方向与前导

ORDER BY 后面每一列都能单独指定升/降和 NULL 位置,互不影响:

-- 先按状态原样排,再按金额降序、空值留最后
SELECT id, status, amount
FROM orders
ORDER BY status ASC, amount DESC NULLS LAST;

排序和索引

如果 ORDER BY 的列上建了索引,PostgreSQL 有时能直接按索引顺序读,省掉一次额外排序。但表达式排序(如 LENGTH(username))一般吃不到普通索引,除非建对应的函数索引。

Warning

排序发生在所有过滤之后。数据量大又没索引可借力时,ORDER BY 可能触发一次全量排序(必要时落盘),是慢查询的常见来源,要留意。

Note

补充一点:ORDER BY 不只用在普通查询里,子查询、CTE、甚至配合 UPDATE / DELETE 也能用。它的核心是”决定返回或处理的顺序”,理解了这个,哪里都能灵活用。

小结

  • ORDER BY 列 默认升序,DESC 改降序。
  • 多列用逗号分隔,前面的列主导排序,后面的列只在前面列相等时生效。
  • 可按 SELECT 里的别名或表达式排序。
  • NULLS FIRST/LAST 控制空值位置,默认 ASC 空值在尾、DESC 空值在头。
  • 列位置数字排序易错,随机排序 RANDOM() 在大表上偏慢。