SQL 对查询结果进行排序(ORDER BY)

学习重点

  • 使用 ORDER BY 子句对查询结果进行排序。

  • ORDER BY 子句中列名的后面使用关键字 ASC 可以进行升序排序,使用 DESC 关键字可以进行降序排序。

  • ORDER BY 子句中可以指定多个排序键。

  • 排序健中包含 NULL 时,会在开头或末尾进行汇总。

  • ORDER BY 子句中可以使用 SELECT 子句中定义的列的别名。

  • ORDER BY 子句中可以使用 SELECT 子句中未出现的列或者聚合函数。

  • ORDER BY 子句中不能使用列的编号。

一、ORDER BY 子句#

截至目前,我们使用了各种各样的条件对表中的数据进行查询。本节让我们再来回顾一下简单的 SELECT 语句(代码清单 27)。

代码清单 27 显示商品编号、商品名称、销售单价和进货单价的 SELECT 语句

1
2
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product;

执行结果

 product_id | product_name  |  sale_price  |  purchase_price
------------+---------------+--------------+----------------
 0001       | T恤衫         |         1000 |             500
 0002       | 打孔器        |          500 |             320
 0003       | 运动T恤       |         4000 |            2800
 0004       | 菜刀          |         3000 |            2800
 0005       | 高压锅        |         6800 |            5000
 0006       | 叉子          |          500 |
 0007       | 擦菜板        |          880 |             790
 0008       | 圆珠笔        |          100 |

对于上述结果,在此无需特别说明,本节要为大家介绍的不是查询结果,而是查询结果的排列顺序。

那么,结果中的 8 行记录到底是按照什么顺序排列的呢?乍一看,貌似是按照商品编号从小到大的顺序(升序)排列的。其实,排列顺序是随机的,这只是个偶然。因此,再次执行同一条 SELECT 语句时,顺序可能大为不同。

KEYWORD

  • 升序

通常,从表中抽取数据时,如果没有特别指定顺序,最终排列顺序便无从得知。即使是同一条 SELECT 语句,每次执行时排列顺序很可能发生改变。

但是不进行排序,很可能出现结果混乱的情况。这时,便需要通过在 SELECT 语句末尾添加 ORDER BY 子句来明确指定排列顺序。

KEYWORD

  • ORDER BY 子句

ORDER BY 子句的语法如下所示。

语法 4 ORDER BY 子句

1
2
3
SELECT <列名1>, <列名2>, <列名3>, ……
  FROM <表名>
 ORDER BY <排序基准列1>, <排序基准列2>, ……

例如,按照销售单价由低到高,也就是升序排列时,请参见代码清单 28。

代码清单 28 按照销售单价由低到高(升序)进行排列

1
2
3
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY sale_price;

执行结果

按照销售单价由低到高(升序)进行排列

不论何种情况,ORDER BY 子句都需要写在 SELECT 语句的末尾。这是因为对数据行进行排序的操作必须在结果即将返回时执行。ORDER BY 子句中书写的列名称为排序键。该子句与其他子句的顺序关系如下所示。

KEYWORD

  • 排序键

▶ 子句的书写顺序

  1. SELECT 子句 → 2. FROM 子句 → 3. WHERE 子句 → 4. GROUP BY 子句 → 5. HAVING 子句 → 6. ORDER BY 子句

法则 15

ORDER BY 子句通常写在 SELECT 语句的末尾。

不想指定数据行的排列顺序时,SELECT 语句中不写 ORDER BY 子句也没关系。

二、指定升序或降序#

与上述示例相反,想要按照销售单价由高到低,也就是降序排列时,可以参见代码清单 29,在列名后面使用 DESC 关键字

KEYWORD

  • 降序

  • DESC 关键字

代码清单 29 按照销售单价由高到低(降序)进行排列

1
2
3
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY sale_price DESC;

执行结果

product_id  | product_name | sale_ price | purchase_ price
------------+--------------+-------------+----------------
 0005       | 高压锅       |        6800 |            5000
 0003       | 运动T恤      |        4000 |            2800
 0004       | 菜刀         |        3000 |            2800
 0001       | T恤衫        |        1000 |             500
 0007       | 擦菜板       |         880 |             790
 0002       | 打孔器       |         500 |             320
 0006       | 叉子         |         500 |
 0008       | 圆珠笔       |         100 |

如上所示,这次销售单价最高(6800 日元)的高压锅排在了第一位。其实,使用升序进行排列时,正式的书写方式应该是使用关键字 ASC,但是省略该关键字时会默认使用升序进行排序。这可能是因为实际应用中按照升序排序的情况更多吧。ASCDESC 是 ascendent(上升的)和 descendent(下降的)这两个单词的缩写。

KEYWORD

  • ASC 关键字

法则 16

未指定 ORDER BY 子句中排列顺序时会默认使用升序进行排列。

由于 ASCDESC 这两个关键字是以列为单位指定的,因此可以同时指定一个列为升序,指定其他列为降序。

三、指定多个排序键#

本节开头曾提到过对销售单价进行升序排列的 SELECT 语句(代码清单 28)的执行结果,我们再来回顾一下。可以发现销售单价为 500 日元的商品有 2 件。相同价格的商品的顺序并没有特别指定,或者可以说是随机排列的。

如果想要对该顺序的商品进行更细致的排序的话,就需要再添加一个排序键。在此,我们以添加商品编号的升序为例,请参见代码清单 30。

代码清单 30 按照销售单价和商品编号的升序进行排序

1
2
3
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY sale_price, product_id;

执行结果

按照销售单价和商品编号的升序进行排序

这样一来,就可以在 ORDER BY 子句中同时指定多个排序键了。规则是优先使用左侧的键,如果该列存在相同值的话,再接着参考右侧的键。当然,也可以同时使用 3 个以上的排序键。

四、NULL 的顺序#

在此前的示例中,我们已经使用过销售单价(sale_price 列)作为排序键了,这次让我们尝试使用进货单价(purchase_price 列)作为排序键吧。此时,问题来了,圆珠笔和叉子对应的值是 NULL,究竟 NULL 会按照什么顺序进行排列呢? NULL 是大于 100 还是小于 100 呢?或者说 5000 和 NULL 哪个更大呢?

请大家回忆一下我们在 算术运算符和比较运算符 中学过的内容。没错,不能对 NULL 使用比较运算符,也就是说,不能对 NULL 和数字进行排序,也不能与字符串和日期比较大小。因此,使用含有 NULL 的列作为排序键时, NULL 会在结果的开头或末尾汇总显示(代码清单 31)。

代码清单 31 按照进货单价的升序进行排列

1
2
3
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY purchase_price;

执行结果

NULL 的顺序

究竟是在开头显示还是在末尾显示,并没有特殊规定。某些 DBMS 中可以指定 NULL 在开头或末尾显示,希望大家对自己使用的 DBMS 的功能研究一下。

法则 17

排序键中包含 NULL 时,会在开头或末尾进行汇总。

五、在排序键中使用显示用的别名#

对表进行分组 中“常见错误 ②”中曾介绍过,在 GROUP BY 子句中不能使用 SELECT 子句中定义的别名,但是在 ORDER BY 子句中却是允许使用别名的。因此,代码清单 32 中的 SELECT 语句并不会出错,可正确执行。

代码清单 32 ORDER BY 子句中可以使用列的别名

1
2
3
SELECT product_id AS id, product_name, sale_price AS sp, purchase_price
  FROM Product
ORDER BY sp, id;

上述 SELECT 语句与之前按照“销售单价和商品编号的升序进行排列”的 SELECT 语句(代码清单 31)意思完全相同。

执行结果

 id  | product_name  |  sp   | purchase_price
------+---------------+-------+---------------
 0008 | 圆珠笔        |   100 |
 0002 | 打孔器        |   500 |            320
 0006 | 叉子          |   500 |
 0007 | 擦菜板        |   880 |            790
 0001 | T恤衫         |  1000 |            500
 0004 | 菜刀          |  3000 |           2800
 0003 | 运动T恤       |  4000 |           2800
 0005 | 高压锅        |  6800 |           5000

不能在 GROUP BY 子句中使用的别名,为什么可以在 ORDER BY 子句中使用呢?这是因为 SQL 语句在 DBMS 内部的执行顺序被掩盖起来了。SELECT 语句按照子句为单位的执行顺序如下所示。

▶ 使用 HAVING 子句时 SELECT 语句的顺序

FROMWHEREGROUP BYHAVINGSELECTORDER BY

这只是一个粗略的总结,虽然具体的执行顺序根据 DBMS 的不同而不同,但是大家有这样一个大致的印象就可以了。一定要记住 SELECT 子句的执行顺序在 GROUP BY 子句之后,ORDER BY 子句之前。因此,在执行 GROUP BY 子句时,SELECT 语句中定义的别名无法被识别 1。对于在 SELECT 子句之后执行的 ORDER BY 子句来说,就没有这样的问题了。

法则 18

ORDER BY 子句中可以使用 SELECT 子句中定义的别名。

六、ORDER BY 子句中可以使用的列#

ORDER BY 子句中也可以使用存在于表中、但并不包含在 SELECT 子句之中的列(代码清单 33)。

代码清单 33 SELECT 子句中未包含的列也可以在 ORDER BY 子句中使用

1
2
3
SELECT product_name, sale_price, purchase_price
  FROM Product
ORDER BY product_id;

执行结果

 product_name  | sale_price  | purchase_price
---------------+-------------+----------------
 T恤衫         |        1000 |            500
 打孔器        |         500 |            320
 运动T恤       |        4000 |           2800
 菜刀          |        3000 |           2800
 高压锅        |        6800 |           5000
 叉子          |         500 |
 擦菜板        |         880 |            790
 圆珠笔        |         100 |

除此之外,还可以使用聚合函数(代码清单 34)。

代码清单 34 ORDER BY 子句中也可以使用聚合函数

ORDER BY 子句中也可以使用聚合函数

执行结果

 product_type  | count
---------------+------
 衣服          |     2
 办公用品      |     2
 厨房用具      |     4

法则 19

ORDER BY 子句中可以使用 SELECT 子句中未使用的列和聚合函数。

七、不要使用列编号#

ORDER BY 子句中,还可以使用在 SELECT 子句中出现的列所对应的编号,是不是没想到?列编号是指 SELECT 子句中的列按照从左到右的顺序进行排列时所对应的编号(1, 2, 3,…)。因此,代码清单 35 中的两条 SELECT 语句的含义是相同的。

KEYWORD

  • 列编号

代码清单 35 ORDER BY 子句中可以使用列的编号

1
2
3
4
5
6
7
8
9
-- 通过列名指定
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY sale_price DESC, product_id;

-- 通过列编号指定
SELECT product_id, product_name, sale_price, purchase_price
  FROM Product
ORDER BY 3 DESC, 1;

上述第 2 条 SELECT 语句中的 ORDER BY 子句所代表的含义,就是“按照 SELECT 子句中第 3 列的降序和第 1 列的升序进行排列”,这和第 1 条 SELECT 语句的含义完全相同。

执行结果

product_id | product_name  | sale_price  | purchase_price
-----------+---------------+-------------+----------------
 0005      | 高压锅        |        6800 |           5000
 0003      | 运动T恤       |        4000 |           2800
 0004      | 菜刀          |        3000 |           2800
 0001      | T恤衫         |        1000 |            500
 0007      | 擦菜板        |         880 |            790
 0002      | 打孔器        |         500 |            320
 0006      | 叉子          |         500 |
 0008      | 圆珠笔        |         100 |

虽然列编号使用起来非常方便,但我们并不推荐使用,原因有以下两点。

第一,代码阅读起来比较难。使用列编号时,如果只看 ORDER BY 子句是无法知道当前是按照哪一列进行排序的,只能去 SELECT 子句的列表中按照列编号进行确认。上述示例中 SELECT 子句的列数比较少,因此可能并没有什么明显的感觉。但是在实际应用中往往会出现列数很多的情况,而且 SELECT 子句和 ORDER BY 子句之间,还可能包含很复杂的 WHERE 子句和 HAVING 子句,直接人工确认实在太麻烦了。

第二,这也是最根本的问题,实际上,在 SQL-92 2 中已经明确指出该排序功能将来会被删除。因此,虽然现在使用起来没有问题,但是将来随着 DBMS 的版本升级,可能原本能够正常执行的 SQL 突然就会出错。不光是这种单独使用的 SQL 语句,对于那些在系统中混合使用的 SQL 来说,更要极力避免。

法则 20

ORDER BY 子句中不要使用列编号。

请参阅#

(完)


  1. 也是因为这一原因,HAVING 子句也不能使用别名。 ↩︎

  2. 1992 年制定的 SQL 标准。 ↩︎

comments powered by Disqus