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

别名就是给列或表起个临时名字,只在这一次查询里有效。 as 可以省,但省了看不出是别名还是漏逗号。 Demo 里 height * 2 AS double_h 把表头从表达式换成人话;带空格的中文别名要用双引号; 表别名 s 和 c 让多表查询短了一半; 最后撞到一件怪事:where 里用别名,这里能过,MySQL 会报 Unknown column。

📝 本章和前面几章的关系

上一篇说「算出来的列没名字」, 这一篇给它起名字。 别名看着是小技巧,其实是可读性的第一道关。 它还有一个隐藏作用: 结果集导出成 CSV 时,表头就是别名—— 给别人看的报表,别名起得好不好直接决定人家看不看得懂。

一、列别名:把表达式换成人话

语法:列名或表达式 AS 别名。 别名的意思就是「这一次查询里,这一列叫这个名字」。 起别名的三个理由:表头好看(height * 2 变成 double_h)、 方便引用(order by 里能用)、方便程序取值(结果集的列名固定)。 别名只在本次查询有效,不会改表结构,也不会存下来。

二、as 能省,但别省

SELECT name n FROM students 也合法,n 就是别名。 但省掉 as 会有歧义:SELECT name n, height h 里, 裸眼看不出 n 是别名还是漏了一个逗号,改代码时容易看错。 本书一律写 as。 多敲四个字符,换来的是「一眼就知道这是别名」。

三、特殊别名:带空格、带中文,要用双引号

别名里有空格、有中文标点、撞上关键字,就得用双引号包起来:AS "学生 姓名"、 AS "身高(厘米)"。 这里的双引号是「标识符引用」的正经用途—— 这也解释了为什么字符串要用单引号:两个引号各有分工,别混。 实务上别名尽量别带空格:程序取值时麻烦(row["学生 姓名"]), 导出 CSV 时表头也会带着空格。 用下划线或驼峰更省事。

四、表别名:多表查询的简称

FROM students AS s JOIN classes AS c 之后,s.name、c.teacher 就代替了全名。 三个好处:少打字、字段重名时能分清(两张表都有 name, 写 s.name 和 c.name 一目了然)、自连接时必须用(同一张表出现两次,不起别名没法区分)。 表别名还有个实际影响: 一旦起了别名,原表名就不能再用了。 FROM students AS s 之后再写 students.name 会报错。

五、别名在 where 里:这里能,MySQL 不能

Demo 实测:WHERE double_h > 340 在这边跑通了,给了 5 行。 但这是 SQLite 的扩展,不是标准 SQL。 同一句搬到 MySQL 上会报 Unknown column 'double_h' in 'where clause'。 因为标准规定:where 里只能引用表里真实存在的列。 为什么会有这条规定:执行顺序。 where 在「算 select 那一列」之前就跑完了,那时别名还不存在;order by 在之后,所以看得见。 规范写法:把表达式重写一遍,或者套一层子查询(外层就能引用内层的别名了)。

六、小结

教材小结:用 as 给列或表起别名,别名让结果更好读;别名带特殊字符时要用引号包起来。 本课补三条:一是别名只在本次查询有效,不改表; 二是 where 里别用别名(这边能过是扩展,MySQL 报错); 三是表别名一旦起了,原表名就不能再用。

七、动手 Demo:别名四种用法,一次撞墙

Demo 六段:列别名、省掉 as、特殊别名、表别名,然后是 where 与 order by 对别名的两种态度。 第五段是这一篇的重点—— 同一句别名,两个子句一个认一个不认,而这里连 where 都认了,那是引擎放宽的结果。

import _q46 as Q
c = Q.con()

c.execute("SELECT name, height * 2 AS double_h FROM students LIMIT 3")
c.execute("SELECT name n, height h FROM students LIMIT 2")        # 省掉 as
c.execute('SELECT name AS "学生 姓名" FROM students LIMIT 2')      # 特殊别名

# 表别名
c.execute("SELECT s.name, c.teacher FROM students AS s "
          "JOIN classes AS c ON s.class_id = c.id LIMIT 3")

# where 里用别名:这里能过(SQLite 扩展),MySQL 报 Unknown column
c.execute("SELECT name, height * 2 AS double_h FROM students "
          "WHERE double_h > 340")
# order by 用别名:标准写法,两边都认
c.execute("SELECT name, height * 2 AS double_h FROM students "
          "ORDER BY double_h DESC LIMIT 3")

实跑输出:

--------------------------------------------------------------------
第十课:别名,以及它在 where 里为什么用不了
--------------------------------------------------------------------

  列别名:给算出来的列起个名
  SQL> SELECT name, height * 2 AS double_h FROM students LIMIT 3
  name     double_h
  ------------------
  张伟     345.00
  王芳     320.40
  李娜     330.00
  (3 行)

  **不起别名,表头就是 `height * 2` 这种表达式;起了别名,表头干净。**

  as 可以省略,但不建议省
  SQL> SELECT name n, height h FROM students LIMIT 2
  n        h
  ----------------
  张伟     172.50
  王芳     160.20
  (2 行)

  **省掉 as 也能过,但一眼看不出是别名还是漏了逗号。** **本书一律写 as。**

  特殊别名:带空格、带中文,要加引号
  SQL> SELECT name AS "学生 姓名", height AS "身高(厘米)" FROM students LIMIT 2
  学生 姓名     身高(厘米)
  ---------------------------
  张伟          172.50
  王芳          160.20
  (2 行)

  **别名里有空格或中文标点,必须用双引号包起来。**
  **这里双引号是「标识符引用」的意思,正好是它的正经用途。**

  表别名:多表查询时的简称
  SQL> SELECT s.name,
       c.teacher FROM students AS s JOIN classes AS c ON s.class_id = c.id LIMIT 3
  name     teacher
  -----------------
  张伟     马老师
  王芳     马老师
  李娜     孔老师
  (3 行)

  **`s` 和 `c` 就是表别名。多表查询里不加别名,字段名一撞就得写全路径。**

  别名能不能用在 where 里:这里能,MySQL 不能
  SQL> SELECT name, height * 2 AS double_h FROM students WHERE double_h > 340
  name     double_h
  ------------------
  张伟     345.00
  刘洋     356.60
  杨帆     350.20
  黄磊     360.40
  周涛     348.00
  (5 行)

  **这一句在这里跑通了,但它是 SQLite 的扩展,不是标准 SQL。**
  **同一句搬到 MySQL 上会报 `Unknown column 'double_h' in 'where clause'`**——
  因为标准规定 where 里只能引用表里真实存在的列。
  **所以别这么写。** 依赖它,代码就绑死在这个引擎上了。

  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 行)

  **order by 用别名是标准的、两边都认。**
  **原因是执行顺序:where 在「算 select 那一步」之前跑完,那时别名还不存在。**
  **order by 在之后,所以看得见。**
  **要按算出来的值筛选,规范写法是把表达式重写一遍,或者套一层子查询。**

第一段列别名。 表头从 height * 2 变成 double_h。 第二段省 as。 能跑,表头是 n 和 h,但可读性差。 第三段特殊别名。 学生 姓名 带空格、身高(厘米) 带中文括号,都得用双引号包。 第四段表别名。 s 与 c 一代,SQL 短了一半。 第五段 where 用别名。 这里给了 5 行、 没报错——但正文里说清了:这是扩展,MySQL 会报 Unknown column。 第六段 order by 用别名。 标准写法,两边都认。

30 秒实验

  1. 把第三段别名里的空格去掉,还能不加引号吗?
  2. (先想再跑)FROM students AS s 之后再写 students.name,会怎样?
  3. 在 MySQL 里想让 where double_h > 340 能跑,该怎么改?

常见误区

  1. 在 where 里用 select 别名。 这边能过是运气,MySQL 直接报 Unknown column。
  2. 表别名起了还用原表名。 起了 s 之后 students.name 就失效了,报的是「找不到表」, 很难联想到是别名惹的。
  3. 别名带空格或中文标点。 程序取值、导 CSV、写文档都别扭,用下划线最省事。
自测三问(点开对答案) 1. 别名会改变表结构吗? —— 不会。别名只在那一次查询的结果集里有效,不写回表,也不存下来。 2. 为什么 order by 能用别名,where 不能? —— 执行顺序不同:where 在算出 select 列之前就执行,别名还不存在;order by 在其后。 3. 表别名起了之后要注意什么? —— 原表名不能再用;字段重名时用别名前缀区分;自连接必须起别名。

列挑完了、名字也起好了,下一篇开始筛行——where,以及那个能让结果差三倍的优先级坑。

相关笔记