# ORDER BY

> 对查询结果排序，打破并列，并控制 NULL 值的位置。

---

LLMS 索引： [llms.txt](/llms.txt)

---

`ORDER BY` 对结果集排序。如果没有它，行的顺序就是未定义的：PostgreSQL 可以按规划器认为方便的任意顺序返回行，而且每次运行的顺序都可能不同。如果应用关心顺序，就要明确指定。

## 本章要点 {#learn}

- 用 `ORDER BY column` 排序
- `ASC`（默认）与 `DESC`
- 多列排序（打破并列）
- 控制空值的位置（`NULLS FIRST` / `NULLS LAST`）
- 按表达式和位置排序

## 示例 {#example}

```sql {num="5.1" caption="ORDER BY"}
-- Default ascending
SELECT tablename
FROM pg_tables
WHERE schemaname = 'pg_catalog'
ORDER BY tablename
LIMIT 4;

-- Descending
SELECT tablename
FROM pg_tables
WHERE schemaname = 'pg_catalog'
ORDER BY tablename DESC
LIMIT 4;

-- Multi-column: sort by schema, then by name within schema
SELECT schemaname, tablename
FROM pg_tables
ORDER BY schemaname, tablename
LIMIT 5;

-- NULLs FIRST / LAST: default for ASC is NULLS LAST, for DESC is NULLS FIRST
SELECT * FROM (VALUES (1), (NULL), (2)) AS t(n) ORDER BY n NULLS FIRST;

-- Sort by an expression
SELECT tablename, length(tablename) AS len
FROM pg_tables
WHERE schemaname = 'pg_catalog'
ORDER BY length(tablename) DESC
LIMIT 3;
```

`ORDER BY tablename` 按升序排序，`ASC` 是默认方向；`DESC` 则反过来。当两行的首个排序键相同时，就用第二个排序键打破并列，以此类推。你可以按任意表达式排序，包括未出现在选择列表中的表达式，也可以按*输出位置*排序（`ORDER BY 1, 2`）；不过，使用列名要好读得多。

空值的处理值得留意：ASC 把空值排在最后，DESC 则排在最前。用 `NULLS FIRST` 或 `NULLS LAST` 可以改变这一默认行为。如果应用无论按哪个方向排序，都希望空值放在最后，就应明确写出来；依赖默认行为，日后有人将 `ASC` 改为 `DESC` 时就容易出错。

## 运行示例 {#run}

运行方式：

```text
$ psql -f source/order-by.sql postgres
 tablename
--------------
 pg_aggregate
 pg_am
 pg_amop
 pg_amproc
(4 rows)

   tablename
-----------------
 pg_user_mapping
 pg_type
 pg_ts_template
 pg_ts_parser
(4 rows)
 ...
```

## 常见陷阱 {#pitfalls}

- 文本排序的结果可能出乎意料：文本排序遵循数据库的*排序规则*（例如 `en_US.UTF-8`），它可能不区分大小写，也可能对重音字符做特殊处理。要按字节顺序排序，可用 `ORDER BY col COLLATE "C"`。
- 使用 `LIMIT` 时忘记 `ORDER BY`：没有 `ORDER BY` 的 `LIMIT 10` 返回的 10 行只是*某些*行，未必是最前或最小的那些。没有确定的顺序，分页就没有意义。
- 首要排序键相同的两行可以按任意顺序出现。将主键作为最后一个排序键来打破并列，就能得到完全确定的结果（`ORDER BY created_at DESC, id DESC`）。
- 对大型结果集排序可能代价很高。如果索引顺序与所需顺序一致，PostgreSQL 可以用索引避免排序。

> [!TIP] 提示
> 列出最近的活动时，按 `created_at DESC, id DESC` 排序。当多行拥有相同时间戳时，`id` 用于打破并列，让分页保持一致。

> [!NOTE] 动手练习
> 添加第二个排序列：用 `ORDER BY schemaname, tablename` 把相同模式的行放在一起，再在各模式内排序。然后按 `length(tablename) DESC` 排序，看看哪个系统目录表的名字最长。

## 下载 {#downloads}

SQL 文件： [order-by.sql](/examples/source/order-by.sql) · [全部示例文件](/examples/examples.zip) · [准备工作](/#prerequisites)
