ORDER BY 不只是排序关键字:SQL排序原理、NULL处理与性能优化实战
2026/9/8 6:09:55 网站建设 项目流程

在数据库管理系统中,ORDER BY大概是“看起来最没门槛”的一个 SQL 功能。很多初学者看 Neso Academy 的数据库管理系统系列课程时,会觉得它只是SELECT语句末尾的一个小尾巴,无非就是升序加个ASC、降序加个DESC。真正把ORDER BY用出问题,往往是在业务上线以后:报表数据忽上忽下、中文排序结果和想象完全不一样、加了排序功能接口突然变慢,还有 SQL 面试里那道“用一条 SQL 取每个部门工资最高员工”的题,十个人里有八个栽在排序设计上。

这篇文章想给出一个明确判断:ORDER BY不只是一个排序关键字,它决定了 SQL 引擎如何交付结果集,是数据展示的“呈现契约”。理解它不能只记语法,而是要理解它的执行时机、排序规则、NULL 值行为、索引使用方式,以及它和分组、去重、窗口函数之间的关系。读完这篇文章,你能独立写出多列排序、NULL 位置控制、中文排序和分组 TopN 查询,也能在遇到慢排序时第一时间知道问题出在哪里。

本文会从这几个角度展开:基础语法与 SQL 执行顺序、多列排序与 NULL 值处理、汉字及特殊类型排序、分组与 TopN 场景、排序性能优化,最后是常见问题排查与工程建议。

1. 这篇文章真正要解决的问题

ORDER BY是 SQL 查询中使用频率极高的子句,但大多数教材只给了它一小节内容。这在学习阶段没有问题,问题出在真实业务里它经常要处理更复杂的情况。

1.1 业务中常见的排序痛点

  • 分页列表要求按某个字段降序,但如果排序字段有大量重复值,翻页时数据会“跳动”。
  • 中文姓名按拼音排序,不同数据库结果不一致。
  • 空值的位置不符合产品预期,有的产品要求空值排最后,默认排序却把空值排在最前。
  • 多字段排序时,次级排序方向写错,导致数据看起来“没排干净”。
  • 对三百万行数据执行ORDER BY,发现走了文件排序,接口响应时间直接翻倍。

这些问题都不是“把ASC改成DESC”就能解决的,它们分别涉及 NULL 语义、排序规则、索引匹配和 SQL 执行顺序。只有把ORDER BY当成一个完整的主题系统学习,才能在面对这些问题时不慌。

1.2 哪些读者最应该读这篇文章

  • 刚开始学 SQL,想打牢排序基础的同学。
  • 准备数据库面试,想一次性理清ORDER BY相关高频考点的求职者。
  • 负责业务报表或后台列表开发,被大数据量排序性能困扰的工程师。
  • 从 MySQL 转到其他数据库,或者刚接触多数据库项目的开发者。

当你觉得“排序有什么好学的”时,这篇文章正好适合你。因为它要讲的不是关键字本身,而是关键字背后的数据库行为。

2. ORDER BY 基础语法与 SQL 执行顺序

2.1 最简单的排序写法

先看一段最基础的 SQL:

SELECT employee_id, employee_name, salary FROM employee ORDER BY salary DESC;

这段 SQL 要做的很简单:查询员工表,按薪资从高到低返回。ASC表示升序,DESC表示降序,不写方向时默认按升序ASC排序。很多初学者会把注意力放在ASC/DESC上,却忽略了更重要的问题:ORDER BY到底是在什么时候执行的。

2.2 SQL 的“逻辑执行顺序”才是理解关键

SQL 语句并不是按照书写顺序从第一行执行到最后一行。现代数据库都会先做语法解析、逻辑改写,再通过优化器生成执行计划。虽然物理执行过程可能和逻辑顺序不完全一致,但理解逻辑执行顺序仍然非常关键,因为它决定了哪些语法是合法的。

一个完整的单表查询,逻辑执行顺序大致是这样的:

执行顺序语句部分作用
1FROM确定数据来源表
2WHERE对原始行做过滤
3GROUP BY对过滤后的行分组
4HAVING对分组后的结果过滤
5SELECT计算并输出目标列
6ORDER BY对输出结果排序
7LIMIT / OFFSET限制返回行数

这里最容易延伸出的一个经典结论是:WHERE 子句中不能直接使用 SELECT 里定义的别名,但 ORDER BY 子句可以。原因就是WHERE在逻辑上先于SELECT执行,它根本看不到SELECT里计算出来的别名;而ORDER BY逻辑上晚于SELECT执行,所以它能引用别名。

-- 正确:ORDER BY 可以使用 SELECT 中的别名 SELECT employee_name, salary * 12 AS annual_salary FROM employee ORDER BY annual_salary DESC; -- 错误:WHERE 中不能使用 SELECT 的别名 SELECT employee_name, salary * 12 AS annual_salary FROM employee WHERE annual_salary > 100000;

第二个查询在多数数据库中会直接报“Unknown column”,如果你在 MySQL 中尝试,也会得到类似错误。正确写法是把annual_salary换算成原始列再放到WHERE条件中。

用一个食堂类比来解释:FROM是准备食材,WHERE是按照套餐要求初筛食材,GROUP BY是把食材分类,SELECT是摆盘,ORDER BY是给成品按顺序编号。摆盘之后你可以决定“这一盘放第三个位置”,但你不能在初筛食材阶段就要求“只保留第三个位置的食材”,因为最终顺序还没产生。这个类比虽然不完全精确,但能帮助理解ORDER BY为什么能使用别名。

3. 排序方向、排序列别名与列位置

3.1 ASC 与 DESC 的默认值

大多数数据库默认升序排序,也就是说ORDER BY salary等价于ORDER BY salary ASC。降序必须显式写DESC。多列排序时,可以分别指定每一列的方向:

SELECT dept_id, employee_name, salary FROM employee ORDER BY dept_id ASC, salary DESC;

这段 SQL 的排序规则是:先按dept_id升序,如果dept_id相同,再按salary降序。这里有一个非常常见的设计误区:有人以为ORDER BY dept_id, salary DESC会把dept_idsalary都按降序排,其实只有紧跟在DESC前面的salary是降序,dept_id仍然是默认升序。如果两列都要降序,必须写成:

ORDER BY dept_id DESC, salary DESC;

这个误区的本质是语法作用域问题。DESC作用于最近的单个排序列,而不是作用于整个子句。

3.2 排序列不一定要出现在 SELECT 列表中

ORDER BY可以按照不在SELECT列表中的列排序。比如用户查询员工姓名列表,但希望按入职日期倒序展示:

SELECT employee_name FROM employee ORDER BY hire_date DESC;

这个语法是合法的,在实际报表中很常见。它的语义很直观:先按hire_date决定最终输出顺序,再返回employee_name。需要注意的是,如果使用了DISTINCT,这条规则会被限制,后面会单独说明。

3.3 按 SELECT 列位置排序的写法

有些老派 SQL 支持ORDER BY 1, 2这种写法,表示按SELECT列表中的第 1 列、第 2 列排序:

SELECT employee_name, salary FROM employee ORDER BY 2 DESC;

这段 SQL 等价于ORDER BY salary DESC。这种写法的问题在于维护性很差,一旦SELECT列表调整顺序,排序逻辑可能悄悄变化,而且超过 3 列时阅读成本极高,代码评审时也不容易发现问题。除非你是在写一些历史遗留脚本,否则不建议在正式项目中使用这种“按位置排序”的写法。

3.4 ORDER BY 与 LIMIT 的结合

分页查询最常用的组合是:

SELECT employee_id, employee_name, salary FROM employee ORDER BY salary DESC LIMIT 10;

这条 SQL 返回薪资最高的前 10 名员工。从逻辑执行顺序看,ORDER BY先对全量结果排序,LIMIT再截取前 10 行。从物理执行看,数据库并不一定会真的把全量结果排序后再截取,它可能使用堆排序或者优先队列,只需要维护前 10 条最值即可。这也是为什么大部分数据库在处理“取 Top N”时不会把整个数据集完整排序。

这里真正容易踩坑的地方是:如果salary有大量重复值,LIMIT每次返回的记录可能不同。比如第 10 名和第 11 名薪资相同,这次查询返回了员工 A,下次可能返回员工 B。要保证分页稳定,可以在ORDER BY中追加一个唯一性高的列,比如员工编号:

ORDER BY salary DESC, employee_id ASC LIMIT 10;

追加唯一列后,相同薪资的记录也有确定顺序,分页结果就不会随机漂移。

4. NULL 值排序、汉字排序与类型隐式转换

4.1 各大数据库的 NULL 排序规则

NULL在 SQL 中代表“未知值”,它既不是 0,也不是空字符串。排序时NULL应该排在最前还是最后,不同数据库有不同默认行为:

数据库默认升序 ASC 时 NULL 的位置默认降序 DESC 时 NULL 的位置
MySQL / SQL Server最前最后
Oracle最后最前
PostgreSQL最后最前

如果你的代码只需要跑在一个数据库上,记住默认行为就行。但如果你的项目涉及多数据库迁移,或者公司内部同时使用多种数据库,就必须注意这种差异。

实际业务中,“空值排最后”是常见需求。在 MySQL 中,如果把排序字段直接写成ORDER BY column ASC,空值会跑在最前面,产品经理一定会提工单。解决办法是利用IS NULL表达式额外加一个排序键:

SELECT employee_name, bonus FROM employee ORDER BY bonus IS NULL ASC, bonus ASC;

这段 SQL 的原理是:bonus IS NULL这个表达式的返回值是10。升序排序时,0在前面,1在后面,所以非空行排在前面,空行排在最后。相同位置的行再按bonus升序排序。这是一个非常实用的小技巧,也是ORDER BY面试中的一个隐藏考点。

4.2 汉字排序没有“铁板一块”的规则

汉字排序是中文业务系统里的高频问题。不同数据库的排序规则差异很大,而且同一数据库的不同版本也可能有区别。

MySQL 中,默认的utf8mb4_general_ciutf8mb4_unicode_ci排序规则对汉字排序的结果不一样。在部分字符集下,MySQL 按汉字编码值排序,结果看起来是“乱序”;要按拼音排序,可以使用CONVERT或强制指定排序规则:

SELECT employee_name FROM employee ORDER BY CONVERT(employee_name USING gbk);

这个写法在 MySQL 环境中经常被用来实现“按拼音排序”的效果。因为 GBK 编码对常用汉字按拼音顺序编排,转成 GBK 之后再排序,结果基本符合拼音顺序。但要注意,生僻字和多音字在这种情况下仍可能出现偏差。

PostgreSQL 的排序规则通常依赖数据库的collation,例如zh_CNzh_CN.utf8;SQL Server 可以显式指定Chinese_PRC_CI_AS排序规则。这里没有办法给出一套通吃所有数据库的写法,更稳妥的做法是:在项目设计阶段就明确业务到底需要按什么顺序展示,如果必须按拼音,就把拼音字段或拼音首字母字段显式冗余到表中,排序时直接用拼音列。靠数据库排序规则去猜“用户想要什么顺序”,在生产环境很容易失控。

4.3 类型隐式转换对排序的影响

当排序列是字符串类型,但里面存放的是数字时,排序结果会令人困惑:

-- 假设 room_no 是 VARCHAR(10),存放 '1'、'2'、'10' SELECT room_no FROM room ORDER BY room_no ASC;

结果可能是:'1'、'10'、'2'。因为字符串排序按字符逐位比较,'10' 的首字符是 '1',所以排在 '2' 前面。要让数值按数学大小排序,就要把字段转成数字:

SELECT room_no FROM room ORDER BY CAST(room_no AS UNSIGNED) ASC;

如果这个表的数据量很大,在ORDER BY中使用函数会导致索引失效,这一点会在后面的性能章节详细展开。从这个例子也能看出,排序问题往往是“存储类型设计不合理”的后续表现。如果有选择,数值就应该用数值类型存,不要在VARCHAR里塞数字。

5. 与 DISTINCT、GROUP BY、窗口函数搭配的排序难点

ORDER BY不只会出现在最简单的查询中,它和DISTINCTGROUP BY以及窗口函数的组合,才真正体现开发者的 SQL 水平。

5.1 DISTINCT 与 ORDER BY 的限制

SELECT中使用DISTINCT去重时,ORDER BY的排序列必须出现在SELECT列表中。这是因为去重之后,结果集已经丢失了那些不在输出列表中的列的对应关系。

-- 错误示例,很多数据库会拒绝执行 SELECT DISTINCT dept_id FROM employee ORDER BY salary; -- 正确写法 SELECT DISTINCT dept_id, salary FROM employee ORDER BY salary;

第一段 SQL 的问题在于:DISTINCT dept_id返回的结果里没有salary,数据库不知道该用哪个salary去排序。第二段 SQL 把salary也放进去重范围,语义就明确了:先按(dept_id, salary)去重,再按salary排序。

5.2 GROUP BY 中 ORDER BY 的使用规则

GROUP BY分组后,SELECT只能输出分组字段和聚合函数。ORDER BY同样有两类用法:

SELECT dept_id, COUNT(*) AS emp_count, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id ORDER BY avg_salary DESC;

这里avg_salary是聚合结果,排序是按分组后的聚合指标排。这个场景很常用,比如“统计各部门平均工资并从高到低展示”。

另一个常见需求是“分组后取每组某字段的最大值或最小值”。比如查每个部门薪资最高的员工,很多人的第一反应是GROUP BY + MAX(salary),但这种写法只能取到最高薪资是多少,无法同时取到这个薪资对应的员工是谁。

-- 只能拿到部门和最高薪资,拿不到员工姓名 SELECT dept_id, MAX(salary) AS max_salary FROM employee GROUP BY dept_id;

如果还要拿到员工姓名,就需要窗口函数,或者使用关联子查询。在目前的主流数据库中,窗口函数是更推荐的方案。

5.3 窗口函数 ROW_NUMBER 与 ORDER BY 的配合

窗口函数是解决“分组 TopN”问题的利器。ROW_NUMBER()会为每一行生成一个从 1 开始的排序编号,这个编号取决于PARTITION BYORDER BY

SELECT dept_id, employee_name, salary FROM ( SELECT dept_id, employee_name, salary, ROW_NUMBER() OVER ( PARTITION BY dept_id ORDER BY salary DESC, employee_id ASC ) AS rn FROM employee ) t WHERE rn = 1;

这段 SQL 的核心思想是:先在内层查询中为每个部门的员工按薪资降序编号,如果薪资相同再按员工编号升序编号,保证编号唯一;外层查询只保留rn = 1的记录,也就是每个部门薪资最高的员工。

如果业务需求是“每个部门薪资前 3 名”,把WHERE rn = 1改成WHERE rn <= 3即可。这种写法比多次关联子查询更容易维护,执行效率通常也更好。窗口函数中的ORDER BY虽然叫ORDER BY,但它只在每个PARTITION BY分区内部生效,不会改变最终输出行的物理顺序。如果你还需要整个结果集按照某种顺序输出,仍然要在外层查询中再写一次ORDER BY

SELECT dept_id, employee_name, salary FROM ( <内层查询同上> ) t WHERE rn = 1 ORDER BY dept_id ASC;

这一步很容易被忽略。窗口函数负责“排名计算”,外层ORDER BY负责“结果展示顺序”,两者职责不同。

6. ORDER BY 性能优化:索引、文件排序与慢查询

6.1 浅析 USING filesort

ORDER BY涉及的性能问题中,最常看到的一个术语是filesort。使用EXPLAIN查看执行计划时,如果Extra一列出现了Using filesort,说明数据库需要额外做一次排序操作。

filesort并不一定代表“慢”,但它是优化时最先关注的信号。当数据量很大时,filesort可能将中间结果写到磁盘临时文件,排序成本显著上升。减少filesort的核心思路,是让索引已经按目标顺序组织数据,数据库直接按索引顺序扫描返回即可,不需要额外排序。

以 MySQL 为例,假设有这样一个索引:

CREATE INDEX idx_dept_salary ON employee (dept_id, salary);

以下查询可以直接利用索引的有序性:

SELECT dept_id, employee_name, salary FROM employee WHERE dept_id = 10 ORDER BY salary DESC;

dept_id = 10用到了索引等值匹配,salary又是索引的第二列,索引在dept_id相同的情况下天然按salary有序,所以数据库可以不经过filesort直接按索引顺序读取数据。

但下面的查询就未必能用上索引:

SELECT dept_id, employee_name, salary, hire_date FROM employee ORDER BY hire_date DESC;

如果索引只建立在(dept_id, salary)上,这个查询无法直接利用索引顺序,很可能会触发filesort。排序列和过滤条件完全不匹配,索引自然帮不上忙。

6.2 最左前缀原则与排序列索引

很多开发者会问:是不是给排序列单独建一个索引就能解决所有排序慢的问题?答案是不一定。多列排序时,索引列的顺序和ORDER BY列顺序必须满足最左前缀匹配。

-- 索引 idx_dept_salary 是 (dept_id, salary) -- 下面的排序能满足索引顺序 ORDER BY dept_id, salary -- 下面的排序不能直接满足,因为跳过了 dept_id ORDER BY salary

ORDER BY dept_id, salary和索引顺序一致,数据库可以直接用索引;ORDER BY salary单独使用第二列,在大多数场景下无法利用该索引的有序性。方向也要注意:ORDER BY dept_id ASC, salary DESC这种混用方向的排序,在部分存储引擎中也不如同方向排序适用。

6.3 覆盖索引与回归测试

所谓覆盖索引,是指索引中已经包含查询需要的全部列,数据库可以只扫描索引而不必回表。举例:

CREATE INDEX idx_dept_salary ON employee (dept_id, salary); SELECT dept_id, salary FROM employee ORDER BY dept_id, salary;

这个查询所需列都在索引中,Extra里可能出现Using index,表示不需要回表读取数据行,性能会明显提升。实际项目中,不可能为每个排序需求都建一个覆盖索引,索引本身也占用写入开销和存储空间,所以要在收益和成本之间平衡。建议在建索引前先用EXPLAIN查看执行计划,对比Using filesort是否消失,再做决定。

6.4 大字段排序与临时表

如果ORDER BY的列包含大字段,比如TEXT或超长VARCHAR,数据库无法完全在内存中排序,可能需要创建临时表并写入磁盘。这种查询的速度很难被索引救回来。工程上更常见的做法是:只查询需要的列,不SELECT *;排序字段尽量选择长度短、值唯一的列;如果业务上必须按大字段排序,可以提前把排序所需的键提取到独立字段中。

6.5 慢 SQL 排序问题的排查路径

当线上反馈“列表页变慢”时,建议按下面的路径排查:

  1. 先在数据库上单独执行慢 SQL,确认是否慢在ORDER BY
  2. EXPLAIN查看执行计划,关注typekeyrowsExtra四列。
  3. 如果Extra中出现Using filesort,尝试调整索引让排序字段满足最左前缀。
  4. 如果数据量确实很大,且查询只需要前 N 条,确认LIMIT是否生效,数据库是否使用优先队列优化。
  5. 检查排序字段是否有大量重复值,考虑追加唯一列排序,避免排序结果抖动。
  6. 确认没有在排序列上使用函数。
  7. 如果表数据量已经很大,考虑归档历史数据,或者把实时列表降级为预处理排序结果表。

这条路径同样适用于 MySQL、PostgreSQL、SQL Server,只是不同数据库的EXPLAIN输出格式不同,核心关注点是一样的。

7. 常见问题与排查思路

下列表格汇总了ORDER BY在实际开发和线排查中经常遇到的问题。

问题现象可能原因排查方式解决方案
中文排序结果不符合预期字符集和排序规则与业务期望不一致查看表字符集、排序规则,测试不同COLLATE显式指定排序规则,或冗余拼音列
分页时数据重复或跳动排序列重复值过多,没有唯一列兜底查看排序字段重复度ORDER BY追加自增主键或其他唯一列
空值位置不对不了解当前数据库的 NULL 排序规则查看数据库文档,小数据量测试使用IS NULL表达式控制空值位置
查询变慢,出现Using filesort排序字段没有可用索引,或使用了函数执行EXPLAIN查看Extra创建合适索引,避免在排序列上使用函数
ORDER BY使用了 SELECT 别名报错某些数据库对别名使用限制更多查看具体数据库版本支持的语法改写为表达式,或使用子查询/派生表
多列排序结果只按第一列排序后几列方向写错或未显式指定检查ORDER BY每个字段的方向每列都显式写ASCDESC
加了DISTINCT后 ORDER BY 报错排序列不在 SELECT 列表中确认排序列是否被去重逻辑排除将排序列加入SELECT,或改写查询
窗口函数排序与结果集顺序不一致混淆了窗口内排序和最终输出排序检查是否缺少外层ORDER BY外层查询补上最终展示顺序

这些内容有些看起来很基础,但在生产环境里,几乎每一个都是真实提过工单的问题。尤其是分页跳变和 NULL 位置,最容易在数据量增大后突然出现。

8. 最佳实践与工程建议

8.1 显式写出排序方向

ORDER BY的默认排序方向是升序,但不要依赖这个默认值。在代码评审中,看到ORDER BY column没有写方向时,应该确认原作者是否真的想要升序。多列排序时,每一列都显式写出方向,可以避免后来的维护者误读。

8.2 分页排序必须加唯一列兜底

不管是传统LIMIT OFFSET分页,还是基于游标的分页,排序字段都建议追加唯一性高的列。比如ORDER BY create_time DESC, id DESC。理由是,create_time在并发插入时很容易相同,只有id就能保证全局顺序唯一。这个习惯能省掉很多分页数据重复问题。

8.3 不要在排序列上套函数

ORDER BY LENGTH(name)ORDER BY YEAR(create_time)这类写法虽然方便,但几乎都会让索引失效。如果业务真的要按函数计算结果排序,优先考虑把计算结果冗余成新列,并给新列建索引。这样查询代码更简单,执行计划也更稳定。

8.4 对 NULL 位置有要求时,主动控制

不要指望所有数据库的 NULL 排序行为一致。如果产品对空值位置有明确要求,就用IS NULL表达式主动指定:

ORDER BY bonus IS NULL ASC, bonus ASC;

这样无论数据库版本怎么变,行为都是确定的。

8.5 先过滤,再排序,最后考虑索引

WHERE条件过滤掉的数据越多,ORDER BY的排序成本越低。这也是为什么有些慢查询不是慢在索引缺失,而是慢在过滤条件本身太宽。优先保证WHERE能走索引,然后看ORDER BY是否能和WHERE共用同一个索引。

比如索引(dept_id, hire_date)可以同时服务WHERE dept_id = 10ORDER BY hire_date,这种组合索引的设计思路,比单独为hire_date建一个排序索引实用得多。

8.6 生产环境变更前先看执行计划

任何涉及排序字段的改动,都不应该在生产环境直接试。先在测试环境验证数据量,用EXPLAIN查看执行计划,确认没有引入新的filesort或者全表扫描。排序索引的建立也要在低峰期执行,因为创建索引本身会占用资源。

8.7 报表场景不要迷信一条 SQL

后台报表如果想按多个条件自由排序,比如用户点击任一列都能排序,不要在 SQL 里拼接不可控的排序表达式。更稳妥的做法是维护一个白名单,只允许用户对固定的几个列排序,并且把列名、排序方向都参数化校验后再拼入 SQL。这样既能防止 SQL 注入,也能保证排序语法始终在可控范围内。

9. 总结与后续学习方向

ORDER BY作为SELECT查询的一部分,经常被低估。它真正影响的是结果集的“确定性”:顺序是否正确、空值位置是否符合预期、分页是否会漂移、性能是否可控。回顾整篇文章,可以重点记住下面几条:

  • 逻辑上ORDER BYSELECT之后执行,所以它能使用别名,而WHERE不能。
  • 多列排序时,方向关键字只作用于最近的一列,每一列都要显式声明。
  • NULL 排序行为在不同数据库间不一致,有明确需求时用IS NULL表达式控制。
  • 汉字排序没有统一规则,生产环境建议冗余拼音列。
  • GROUP BY + MAX只能取到最值,无法稳定取到对应行,ROW_NUMBER()窗口函数才是分组 TopN 的正解。
  • 排序性能优化要围绕索引设计,EXPLAIN中的Using filesort是重点预警信号。
  • 任何分页查询都要在排序里追加唯一列,保证翻页稳定。

下一步建议练习三个方向:

  1. 手写一个分组 TopN 的窗口函数查询,并对比GROUP BY方案的区别。
  2. 在本地 MySQL 数据库中创建一张十万行测试表,给不同字段建索引,用EXPLAIN查看排序执行计划。
  3. 阅读你所用数据库官方文档中关于“排序优化”和“NULL 值排序”的章节,因为ORDER BY的底层实现,不同版本之间仍有细节差异。

排序这个功能看起来简单,但它是 SQL 语言里最贴近“用户体验”的一个子句。前端列表好不好用、报表数据稳不稳定、接口响应快不快,很多问题追到根上,都会落在ORDER BY这一行。把这一行写稳,是数据库开发和后端开发的基本功。建议把上面代码存成笔记,下次写报表或者做 SQL 面试复习时直接翻出来对照使用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询