ORDER BY:排序
本教程共 50 篇 · 第 19 篇 · 更新于 2026-07-31
19. ORDER BY:排序
本节目标:学完本章你能用 ORDER BY 对结果做升序/降序排列,能在多列上组合排序,并清楚 NULL 值会排在哪儿、怎么用 NULLS FIRST/LAST 控制它。
你有没有发现,前面 SELECT 出来的行顺序,好像跟你插入的顺序”差不多”,但又说不准?这不是错觉,而是 SQLite 的一个基本事实:表里的行本身没有保证的顺序,SELECT 不带 ORDER BY 时返回的顺序是”未定义”的——今天和明天可能一样,换台机器、换个版本可能就变了。想得到稳定、可读的排列(比如按年龄从小到大、按金额从高到低),必须显式写上 ORDER BY。这也是 serverless、零配置数据库的一个体感:没有独立服务进程帮你”维持顺序”,顺序完全由你这条查询决定。
ORDER BY 的基本写法是在 SELECT 的 FROM(和 WHERE)之后,跟上”排序列 方向”:
SELECT name, age
FROM users
ORDER BY age ASC;
ASC 表示升序(从小到大),DESC 表示降序(从大到小)。如果不写方向,SQLite 默认按 ASC 处理。所以 ORDER BY age 和 ORDER BY age ASC 完全等价。
降序很常用,比如”找出最年长的几位用户”:
SELECT name, age
FROM users
ORDER BY age DESC;
sqlite> SELECT name, age FROM users ORDER BY age DESC LIMIT 3;
Dave|40
Alice|30
Carol|30
注意这里 Alice 和 Carol 都是 30 岁,它们谁在前谁在后?答案是”不确定”——当排序列值相同时,SQLite 不保证它们之间的先后。这就引出多列排序。
多列排序:当第一列出现并列时,用第二列来定先后。写法是用逗号分隔多列,每列可以单独指定 ASC/DESC:
SELECT name, age
FROM users
ORDER BY age DESC, name ASC;
这条语句先按年龄从大到小排;年龄相同的,再按姓名升序(A→Z)排。于是两个 30 岁的人会被稳定地排成 Carol、Alice。记住一个关键规则:ORDER BY 从左到右依次生效,最左边的列是”主排序键”。把你想优先决定的列放在最左,次级的放右边,顺序错了结果就完全不是你要的。
还可以只排某一列、但 SELECT 别的列——SQLite 允许排序键不出现在 SELECT 列表里。例如按邮箱排序但只显示姓名:
SELECT name FROM users ORDER BY email;
这一点在调试时很有用,但也要小心:排序键若包含 NULL,行为要单独说。
NULL 在排序里的位置是个经典坑。SQLite 把 NULL 视作”比任何值都小”。因此升序(ASC)时,NULL 会排在最前面;降序(DESC)时,NULL 排到最后面。回到我们的 users 表,假设 Carol 的 email 是 NULL(未知),那么 ORDER BY email ASC 时 Carol 会第一个冒出来,这可能不是你想要的。
从 SQLite 3.30.0 起(我们的 3.53.4 基线当然包含),可以用 NULLS FIRST / NULLS LAST 显式指定 NULL 的落点,覆盖默认行为:
SELECT name, email
FROM users
ORDER BY email NULLS LAST;
这样无论升序降序,NULL 都被强制放到最后。想让它永远在最前,就用 NULLS FIRST。当你做报表、导出列表时,把 NULL 固定到末尾通常是更友好的选择。
ORDER BY 常和 LIMIT 搭档。比如”年龄最大的前 3 名”就是 ORDER BY age DESC 再 LIMIT 3。但要注意顺序:ORDER BY 必须写在 LIMIT 前面,SQLite 会先排序、再截取前 N 行。如果写成 LIMIT 在前(语法不允许),逻辑就反了。排序后再分页(第 20 章)更是标配组合。
重点提示
没有 ORDER BY,返回顺序就是”未定义”——不要依赖任何你观察到的顺序。凡是面向用户的列表、报表、排行榜,都必须显式 ORDER BY,否则哪天顺序变了你都查不出原因。
实用技巧
多列排序时,把”最想优先决定”的列放最左边。想做排行榜(先按分数降序,分数相同按时间升序),就写成 ORDER BY score DESC, created_at ASC,主次要清晰。
常见坑
NULL 在排序里比任何值都小,升序排最前、降序排最后,常常打乱预期。需要稳定地把空值放末尾,请用 NULLS LAST(SQLite 3.30.0+,3.53.4 基线已支持)。另外,ORDER BY 排的是”查询出来的结果”,不会改动表里数据的物理顺序。
除了写列名,ORDER BY 还支持用”列在 SELECT 列表里的位置序号”来排序,例如 ORDER BY 2 表示按 SELECT 的第二列排。这种写法省事,但可读性差、容易在改 SELECT 列表后悄悄排错列,生产代码里不推荐,了解即可。
再来对比”在 SQL 里排序”和”把数据拉回程序里排序”:后者要先 SELECT 把所有行传回内存再排,数据量大时既占带宽又占内存;前者让数据库直接吐出排好的结果,效率高得多。所以只要排序规则能用 SQL 表达,优先用 ORDER BY。
为什么默认是升序而不是”原顺序”?因为原顺序本就未定义,SQLite 必须给一个确定语义,升序是最自然、最可预测的选择。也正因为并列时顺序不确定,做”排行榜""最新列表”这类对顺序敏感的展示时,强烈建议多加一列唯一键(如 id)做兜底排序,避免出现”同分用户今天甲在前、明天乙在前”的诡异抖动。
适用场景再举几个:论坛按发帖时间倒序看最新、商品按销量倒序做榜单、学生按成绩排序发奖、日志按级别过滤后再按时间排。凡是”先排好再看”的需求,ORDER BY 都是第一选择。
再多说一句性能:ORDER BY 在没有合适索引时,SQLite 会把结果集拉到内存里做一次排序;如果排序列上建有索引,数据库可以直接按索引顺序读取,省掉这次排序开销。所以”经常按某列排序的大表”考虑建索引(第 34 章),是让排序变快的正道。当然对教程阶段的小表,直接 ORDER BY 毫无压力,不必过早优化。
类比小结:ORDER BY 就像整理书架——你可以按书名拼音(ASC)、按出版年份倒序(DESC),或者先按作者再按书名(多列)。而 NULL 那本书没有年份标签,默认会被塞到最前面或最后面,除非你专门说”没标签的放哪”。
什么时候用 ORDER BY?几乎任何”给人看”的查询都要:通讯录按姓名、订单按金额、日志按时间、榜单按分数。养成”SELECT 出来就想排序”的习惯,你的查询结果会立刻专业很多。