🐬 第5章 排序检索数据
📄 一句话概括

不写 ORDER BY,数据库返回的顺序就没有保证——上一章的 13 行是「它高兴的顺序」。 这一章给它加上顺序:单列排、多列接力排、升序降序。 Demo 里三处关键结果: 按价格降序取第一行是 JetPack 2000、55.00。 10.00 那两个产品(Bird seed 与 TNT (5 sticks))靠第二个列才分出先后。 而 DESC 只作用在紧挨着它的那一列,要两列降序就得写两个 DESC。

📝 本章和前面几章的关系

接上一章:取列学会了,但取出来的行是乱序的。这一章只讲一件事——排序。 这是全书最短的一章(四页),但它管的是一条铁律: 凡是要「前几」「最新」「最大」的地方,背后都是 ORDER BY。 第 12 章的 MAX() 只能给一个数, 给不了整行;要整行就得靠这一章。

一、为什么必须自己排序

关系数据库里,行没有顺序。 表不是数组,行不占「第几个位置」。 你插进去的顺序、主键的大小,都不构成返回顺序的承诺。 那为什么平时看着有顺序? 因为小表的数据在磁盘上恰好是顺着放的。 表一大、加了索引、走了不同的执行计划,顺序立刻变。 「以前一直是这个顺序」是最贵的错觉。 所以要顺序,就得明写 ORDER BY。 这不是「加个保险」,是唯一能保证顺序的手段。

二、ORDER BY 基本用法

SELECT prod_name FROM products ORDER BY prod_name; ORDER BY 写在 FROM 后面(有 WHERE 就写在 WHERE 后面)。 ORDER BY 后面写的是「按哪个列排」,这个列不必出现在 SELECT 里。 比如 SELECT prod_name FROM products ORDER BY prod_price 完全合法——按价格排,但只显示名字。 默认是升序(ASC)。 要降序才写 DESC。 数字按大小、文本按字符序(这跟字符集与校对有关,第 27 章讲)、日期按先后。 排序发生在取数之后、LIMIT 之前。 顺序是: 筛行(WHERE)→ 分组(GROUP BY)→ 选列(SELECT)→ 排序(ORDER BY)→ 限行(LIMIT)。 这就是为什么 ORDER BY 能用 SELECT 里起的别名,而 WHERE 不能。

三、按多个列排序

ORDER BY prod_price, prod_name 意思是:先按价格排,价格相同的那些行再按名字排。 Demo 里 10.00 有两行(Bird seed 与 TNT (5 sticks)), 它们的先后完全由第二个列决定:B 在 T 前面。 如果只写 ORDER BY prod_price, 这两行的先后就是随机的。 多列排序的实际用途:消除不确定性。 分页时如果排序键有重复值, 翻页就可能出现「同一行出现两次」或者「某行从没出现」。 加一个唯一列(比如主键)当最后一个排序键,这个问题就没了。 写几个列都行,但通常两三个就够。列越多,排序越慢。

四、指定排序方向:DESC 的作用域

DESC 只作用在紧挨着它的那一列。 这是 ORDER BY 最容易错的地方。 ORDER BY prod_price DESC, prod_name ——价格降序, 价格相同时名字仍然升序(因为 prod_name 后面没写 DESC)。 ORDER BY prod_price DESC, prod_name DESC ——这才两列都降序。要几列降序,就写几个 DESC。 还有一个组合必须记住:ORDER BY 列 DESC LIMIT 1 就是「找最大的那一行」。 Demo 里它给出 JetPack 2000、55.00——整行信息都在,而不只是一个数。 这是「取 Top N 兼取整行」的标准写法。

五、小结

教材小结一句话:ORDER BY 排序,DESC 降序,可以多列。 本课补三条:一是行序不保证是默认状态,不写 ORDER BY 就等于放弃顺序; 二是 DESC 只作用于紧挨着它的那一列; 三是 ORDER BY 加 LIMIT 1 是「取最大值那一行」的标准写法。

六、动手 Demo:从乱序到有序,再到「取最大的那一行」

Demo 走四步:单列升序、多列接力、单列降序、两列降序, 最后用一个组合收尾——ORDER BY 加 LIMIT 1 把最贵的产品整行取出来。 重点看 10.00 那两行:它在四个结果里的位置不一样,这就是「第二个列在起作用」。

import sqlite3
con = sqlite3.connect(":memory:")

con.execute("SELECT prod_name FROM products ORDER BY prod_name")
con.execute("SELECT prod_id, prod_price, prod_name FROM products "
            "ORDER BY prod_price, prod_name")          # 多列接力
con.execute("SELECT prod_id, prod_price FROM products "
            "ORDER BY prod_price DESC LIMIT 5")        # 降序
con.execute("SELECT prod_name, prod_price FROM products "
            "ORDER BY prod_price DESC LIMIT 1")        # 最贵的那一行

实跑输出:

--------------------------------------------------------------------
第五课:排序检索数据
--------------------------------------------------------------------

  【一】排序数据:ORDER BY
  SQL> SELECT prod_name FROM products ORDER BY prod_name
  prod_name
  ---------------
  .5 ton anvil
  1 ton anvil
  2 ton anvil
  Bird seed
  Carrots
  Detonator
  Fuses
  JetPack 2000
  Oil can
  Safe
  Sling
  TNT (1 stick)
  TNT (5 sticks)
  (13 行)
  **ORDER BY 后面写的是「按哪个列排」,这个列不必出现在 SELECT 里。**
  SQL> SELECT prod_name, prod_price FROM products ORDER BY prod_price
  prod_name          prod_price
  ------------------------------
  Carrots            2.50
  TNT (1 stick)      2.50
  Fuses              3.42
  Sling              4.49
  .5 ton anvil       5.99
  Oil can            8.99
  1 ton anvil        9.99
  Bird seed          10.00
  TNT (5 sticks)     10.00
  Detonator          13.00
  2 ton anvil        14.99
  Safe               50.00
  JetPack 2000       55.00
  (13 行)
  价格从 2.50 排到 55.00,**默认是升序(ASC)。**
  **排序是「查出来之后」才发生的**,所以它几乎总是 SQL 的最后一步(LIMIT 除外)。

  【二】按多个列排序
  SQL> SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price, prod_name
  prod_id     prod_price     prod_name
  ------------------------------------------
  FC          2.50           Carrots
  TNT1        2.50           TNT (1 stick)
  FU1         3.42           Fuses
  SLING       4.49           Sling
  ANV01       5.99           .5 ton anvil
  OL1T        8.99           Oil can
  ANV02       9.99           1 ton anvil
  FB          10.00          Bird seed
  TNT2        10.00          TNT (5 sticks)
  DTNTR       13.00          Detonator
  ANV03       14.99          2 ton anvil
  SAFE        50.00          Safe
  JP2000      55.00          JetPack 2000
  (13 行)
  **价格相同时才看第二个列。** 10.00 那两行(Bird seed 与 TNT (5 sticks))
  就是靠 prod_name 定的先后:B 在 T 前面。**去掉第二个列,这两行的先后就是随机的。**
  如果只写 ORDER BY prod_price,这两行的顺序是任意的——**加第二列就是为了消除这种任意性。**

  【三】指定排序方向:DESC
  SQL> SELECT prod_id, prod_price FROM products ORDER BY prod_price DESC LIMIT 5
  prod_id     prod_price
  -----------------------
  JP2000      55.00
  SAFE        50.00
  ANV03       14.99
  DTNTR       13.00
  FB          10.00
  (5 行)
  **DESC 只作用在紧挨着它的那一列**,这一点最容易错。
  SQL> SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name
  prod_id     prod_price     prod_name
  ------------------------------------------
  JP2000      55.00          JetPack 2000
  SAFE        50.00          Safe
  ANV03       14.99          2 ton anvil
  DTNTR       13.00          Detonator
  FB          10.00          Bird seed
  TNT2        10.00          TNT (5 sticks)
  ANV02       9.99           1 ton anvil
  OL1T        8.99           Oil can
  ANV01       5.99           .5 ton anvil
  SLING       4.49           Sling
  FU1         3.42           Fuses
  FC          2.50           Carrots
  TNT1        2.50           TNT (1 stick)
  (13 行)
  上句:价格降序,价格相同时名字**仍是升序**(因为 prod_name 后面没写 DESC)。
  SQL> SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC,
       prod_name DESC
  prod_id     prod_price     prod_name
  ------------------------------------------
  JP2000      55.00          JetPack 2000
  SAFE        50.00          Safe
  ANV03       14.99          2 ton anvil
  DTNTR       13.00          Detonator
  TNT2        10.00          TNT (5 sticks)
  FB          10.00          Bird seed
  ANV02       9.99           1 ton anvil
  OL1T        8.99           Oil can
  ANV01       5.99           .5 ton anvil
  SLING       4.49           Sling
  FU1         3.42           Fuses
  TNT1        2.50           TNT (1 stick)
  FC          2.50           Carrots
  (13 行)
  这一句才是两列都降序。**要几列降序,就得写几个 DESC。**

  【四】用 ORDER BY 找「最大的那个」
  SQL> SELECT prod_name, prod_price FROM products ORDER BY prod_price DESC LIMIT 1
  prod_name        prod_price
  ----------------------------
  JetPack 2000     55.00
  (1 行)
  **ORDER BY + LIMIT 1 就是「最大的那一行」**,而且它带回了整行信息。
  第 12 章会讲 MAX(),但 MAX() 只能给你一个数,给不了整行——**要整行就用这一招。**

第一段是单列升序。 13 行按名字排, 注意 .5 ton anvil 排在第一个——标点符号在字符序里比字母和汉字都靠前,这不是 bug。 第二段是多列接力。 价格为主、名字为辅。10.00 那两行的先后由名字决定(Bird seed 在 TNT 前面); 如果把第二个列去掉,这两行谁先谁后就看数据库心情了。 第三段是单列降序。 55.00、50.00、14.99、13.00、10.00——注意 10.00 只出现一次, 因为 LIMIT 5 把第二行 10.00 砍掉了。 第四段是这一章的关键。 前一句只给 prod_price 加 DESC,所以价格降序、价格相同时名字升序; 后一句两个列都加 DESC,10.00 那两行就翻了个个儿(TNT 在前)。同一份数据,两个 DESC,两种顺序。 最后一段是实用收尾。 ORDER BY prod_price DESC LIMIT 1 把 JetPack 2000 整行取出来—— 名字、价格一起给你。第 12 章的 MAX() 只能给你 55.00 这个数,给不了产品名。

30 秒实验

  1. 把 ORDER BY prod_name 换成 ORDER BY prod_name DESC,第一行变成什么?
  2. 试 ORDER BY vend_id, prod_price DESC——同一供货商内部价格降序,供货商之间升序,结果对吗?
  3. 不加 ORDER BY 直接 LIMIT 1,多跑几次,第一行会变吗?变或不变各说明什么?

常见误区

  1. 以为只写一个 DESC 就全降序。DESC 只作用于紧挨着它的那一列。要两列降序就得写两个 DESC。
  2. 以为排序键必须在 SELECT 里。不必。ORDER BY 可以用没取出来的列。 这一点和 DISTINCT 配合时会出问题(DISTINCT 之后不能按没选的列排)。
  3. 用非唯一列排序后分页。排序键有重复值时,翻页会出现重复行或漏行。 加主键当最后一个排序键。
自测三问(点开对答案) 1. 不写 ORDER BY 时,行的顺序由什么决定? —— 没有保证。取决于存储顺序、索引、执行计划。可以说「由数据库决定」,但不能当成规则来依赖。 2. `ORDER BY a DESC, b` 和 `ORDER BY a DESC, b DESC` 有什么区别? —— 前者 a 降序、a 相同时 b 升序;后者 a、b 都降序。DESC 只作用于紧挨着它的那一列。 3. 怎么取「价格最高的那个产品的名字和价格」? —— `SELECT prod_name, prod_price FROM products ORDER BY prod_price DESC LIMIT 1`。 用 MAX() 只能拿到价格,拿不到名字。

能排能取了,但每次都是整表。 下一章开始筛:WHERE 子句、九种操作符、BETWEEN 的闭区间, 以及那个反直觉的规矩——NULL 不能用等号判断。

相关笔记