🦏 第3章 查询操作
📄 一句话概括

order by 决定结果按什么顺序出来:单列、多列、升序降序、按别名、按序号。 它永远放在语句最后(limit 之前)。 Demo 里 ORDER BY height DESC 把 180.20 排到最前;多列排序先按班级、同班再按身高; 中文按名字排出来是「刘吴周孙张徐」——按编码,不是拼音。

📝 本章和前面几章的关系

上一篇筛行,这一篇定顺序。 两件事合起来,才叫「查出一份能看的结果」。 它和下一篇的 limit 是一对:不排序就分页,翻页必然乱——这两篇要连着看。

一、升序与降序:asc 能省,desc 不能

ORDER BY 列名 默认升序(从小到大),等价于 ORDER BY 列名 ASC。 降序必须写 DESC。 数字按大小、字符串按校对规则、日期按时间先后—— 前提是日期存成了标准格式,否则按字典序排就乱了。 ASC/DESC 只作用于紧跟它的那一列,不影响别的列。

二、多列排序:先按第一列,撞了再看第二列

ORDER BY class_id, height DESC:先按班级升序,同班的再按身高降序。 写在后面的列只在前面「打平」时才起作用——这就是「排序的优先级」:靠前的说了算。 实务tip:排序的列最好是「有索引的列 + 唯一列」组合, 否则同分的行之间顺序不保证,翻页会抖。

三、中文排序:按编码,不是按拼音

Demo 排出来是「刘、吴、周、孙、张、徐」——这是 UTF-8 编码序,既不是拼音,也不是笔画。 MySQL 默认的 utf8mb4_0900_ai_ci 对中文也是按编码权重排, 同样不是拼音序。 想要拼音序只有两条路。 第一条:ORDER BY CONVERT(name USING gbk)——GBK 编码恰好接近拼音序, 但它只对常用汉字成立,生僻字会乱。 第二条(生产里更常见):另存一个 name_pinyin 字段,插入时算好拼音,排序直接用。 可控、也快,还能处理多音字。

四、按别名、按序号——能用,但别依赖

按别名:ORDER BY double_h DESC,标准写法,两边都认。 按序号:ORDER BY 2 表示「按第二列」。 能跑,但极其危险:以后在 select 里加一列,序号指的东西就变了,排序悄悄错掉。 推荐做法:永远写列名或别名,不写序号。

五、三条要特别注意的

一、NULL 的位置: 升序时 NULL 排最前,降序时排最后——引擎把 NULL 当最小值看(MySQL 也是)。 要固定位置就写 ORDER BY 列 IS NULL, 列。 二、位置固定:order by 在 where/group by/having 之后、limit 之前。 写错位置是语法错,不是逻辑错。 三、排序有代价:数据量大时它要占内存、甚至写临时文件。 能靠索引顺序直接拿到的,就别再排一遍—— ORDER BY 主键 常常是免费的,ORDER BY 函数(列) 一定不免费。

六、小结

教材小结: order by 指定排序列与方向(asc/desc),可以多列排序;中文按编码排序,需要拼音序要另行处理。 本课补三条:一是 desc 只作用于紧跟的那一列; 二是中文默认按编码排,拼音序要 gbk 转换或另存拼音列;三是别用序号排序,加一列就错。

七、动手 Demo:从升序降序到中文那五个字

Demo 六段:单列升降、多列排序、中文排序、按别名排、按序号排,最后讲三条注意事项。 第三段是这一篇的重点——中文排出来的顺序会让第一次见的人吃一惊。

import _q46 as Q
c = Q.con()

c.execute("SELECT name, height FROM students ORDER BY height DESC LIMIT 4")
c.execute("SELECT name, height FROM students ORDER BY height LIMIT 2")
# 多列:先按班级,同班按身高降序
c.execute("SELECT class_id, name, height FROM students "
          "ORDER BY class_id, height DESC")
# 中文:按编码排,不是拼音
c.execute("SELECT name FROM students ORDER BY name LIMIT 6")
# 别名与序号
c.execute("SELECT name, height * 2 AS double_h FROM students "
          "ORDER BY double_h DESC LIMIT 3")
c.execute("SELECT name, height FROM students ORDER BY 2 DESC LIMIT 3")

实跑输出:

--------------------------------------------------------------------
第十二课:order by,以及中文到底按什么排
--------------------------------------------------------------------

  升序降序:asc 可以不写,desc 必须写
  SQL> SELECT name, height FROM students ORDER BY height DESC LIMIT 4
  name     height
  ----------------
  黄磊     180.20
  刘洋     178.30
  杨帆     175.10
  周涛     174.00
  (4 行)

  SQL> SELECT name, height FROM students ORDER BY height LIMIT 2
  name     height
  ----------------
  陈静     158.60
  王芳     160.20
  (2 行)

  **默认是 asc(升序),所以升序一般省掉不写;降序必须写 desc。**

  多列排序:先按第一列,撞了再按第二列
  SQL> SELECT class_id, name, height FROM students ORDER BY class_id, height DESC
  class_id     name     height
  -------------------------------
  1            杨帆     175.10
  1            周涛     174.00
  1            张伟     172.50
  1            王芳     160.20
  2            刘洋     178.30
  2            李娜     165.00
  2            吴倩     163.50
  2            赵敏     162.80
  3            黄磊     180.20
  3            徐明     169.70
  3            孙悦     161.40
  3            陈静     158.60
  (12 行)

  **先按班级升序,同班的按身高降序。** **写在后面的列只在前面「打平」时才起作用。**

  中文排序:按编码排,不是按拼音
  SQL> SELECT name FROM students ORDER BY name LIMIT 6
  name
  -----
  刘洋
  吴倩
  周涛
  孙悦
  张伟
  徐明
  (6 行)

  **看到的是 `UTF-8 编码序`,不是拼音序,也不是笔画序。**
  **MySQL 默认的 `utf8mb4_0900_ai_ci` 对中文也是按编码权重排,同样不是拼音。**
  **真要拼音序怎么办**:
  **MySQL 可以 `ORDER BY CONVERT(name USING gbk)`**(GBK 的编码恰好接近拼音序),
  **或者干脆存一个 `name_pinyin` 字段专门排序。** **后者在生产里更常见——可控、也快。**

  order by 能用别名,也能用列序号
  SQL> SELECT name, height * 2 AS double_h FROM students ORDER BY double_h DESC LIMIT 3
  name     double_h
  ------------------
  黄磊     360.40
  刘洋     356.60
  杨帆     350.20
  (3 行)

  SQL> SELECT name, height FROM students ORDER BY 2 DESC LIMIT 3
  name     height
  ----------------
  黄磊     180.20
  刘洋     178.30
  杨帆     175.10
  (3 行)

  **`ORDER BY 2` 是「按第二列排」,能跑,但千万别写进代码**——
  **以后在 select 里加一列,序号指的东西就变了。**

  三条要特别注意的
  **一、null 的位置**:升序时 null 排最前,降序时排最后。 MySQL 也是这个规矩(null 当最小值看)。
  **二、order by 一定要放在语句最后**,在它后面只能再跟 limit。
  **三、排序有代价**:数据量大时它要占内存甚至写临时文件,**能靠索引顺序拿到的,就别再排一遍。**

第一段降序。 黄磊 180.20、刘洋 178.30、杨帆 175.10、周涛 174.00。 第二段升序。 陈静 158.60、王芳 160.20——最矮的两个在前面。 第三段多列。 先按班级,一班里再按身高降序——两个条件叠在一起,看输出就能验证。 第四段中文。 刘、吴、周、孙、张、徐,是编码序:刘 U+5218、吴 U+5434、周 U+5468, 越往后码位越大。 第五段别名与序号。 两种都能排,但序号那种写法别进代码。 第六段三条注意。 NULL 的位置、子句位置、排序的代价。

30 秒实验

  1. ORDER BY class_id DESC, height 与 ORDER BY class_id, height DESC 差在哪?
  2. (先想再跑)ORDER BY phone 升序,没填手机号的孙悦排在哪?
  3. 把 ORDER BY 2 DESC 里的 2 改成 3,排的是哪一列?

常见误区

  1. 以为中文会按拼音排。 默认按编码,排出来是「刘吴周孙张徐」这种顺序。
  2. 用序号排序。 ORDER BY 2 在加了一列之后指向别处,排序悄悄错。
  3. 只按非唯一列排序还分页。 同分的行顺序不保证,翻页时同一行可能出现两次。
自测三问(点开对答案) 1. 多列排序时,后面的列什么时候起作用? —— 只在前面所有列的值都相等(打平)时才起作用;靠前的列优先。 2. 中文默认按什么排?想要拼音序怎么办? —— 按编码(UTF-8 字节序)排,不是拼音; 可以 ORDER BY CONVERT(name USING gbk),或另存一个拼音字段。 3. NULL 在排序里排在哪? —— 当最小值看:升序排最前,降序排最后。

顺序定了, 但一查就是十几万行,谁也不想一次看完。 下一篇 limit:只取前几行,以及分页那条公式。

相关笔记