🛢️ 第9章 数据查询
📄 一句话概括

字段别名只改输出那一层,表别名是「把原来的名字换掉」,不是追加。 同一张表出现两次,别名是语法强制要求,不起就报「列名有歧义」。 表别名整句都能用,字段别名只能用在 SELECT 之后的子句里。

📝 本章和前面几章的关系

9.7 只有两个小节:为表取别名、为字段取别名。语法极简,但可见范围容易记错。 这一篇和 9.1 的执行顺序是同一件事的两面:9.1 用别名去验顺序,这里讲别名本身。

一、字段别名

字段别名的作用是改结果集的列名,表里的列名一点没变。 所以它只发生在输出那一层,不改变任何数据。

SELECT price * 1.1 AS 新价, price * 1.1 "新 价" FROM tb_goods;

AS 可以不写,直接空格加别名也行。 别名里有空格或中文,得用双引号包起来,不然会被当成两个词。 MySQL 里通常写反引号,沙箱里认双引号——这是引号习惯的差异,不是语义差异。 跨库搬 SQL 时这一处最容易被改坏,因为两边的引号规则不一样。 字段别名最实际的用处是给计算列起名。 price * 1.1 这种列在结果集里没有名字,导出报表时就是一片空白。

二、表别名

表别名是给整张表起个短名字,好处是少打字,尤其是列名长、表名也长的时候。 它更大的用处是「同一张表出现两次时区分开」。 一旦给表起了别名,原表名在这一句里就不能再用了。 写 SELECT tb_emp.name FROM tb_emp e 会报 no such column。 所以别名不是「再额外加一个名字」,而是「把原来的名字换掉」。 这一点和字段别名不一样:字段别名是并列的,原名和新名都在。

三、为什么自连接必须起别名

同一张表并排放两次,引擎没法区分「这一列是哪一张的」。 不起别名时,SELECT name FROM tb_emp, tb_emp 报 ambiguous column name: name。 这不是风格问题,是语法要求。 所以看到自连接,第一件事就是给它俩各起一个短名字:e 和 m,员工和上级。 别名起得好的自连接,读起来像两张不同的表。 起得差(比如 t1 t2),读的人得反复回看 ON 才知道谁是谁。

四、别名盖住原列名时会怎样

别名和原列名重名是允许的,而且排在 SELECT 之后的子句里,别名优先。 所以 SELECT price AS stock, stock FROM tb_goods ORDER BY stock 排的是 price。 想排真正的 stock 列,得写 ORDER BY 2(按列序号),结果立刻不一样。 这种写法本身就该避免:查错数据时最难查的就是「名字一样、意思不同」。 别名要么改得不像列名,要么干脆别用。

五、两种别名的可见范围

表别名在整句里都能用,WHERE 里也行,因为它是在 FROM 那一层就绑定的。 字段别名不一样,它是在 SELECT 那一层才产生的。 沙箱里字段别名放到 WHERE、GROUP BY、ORDER BY 都能用,因为 SQLite 放宽了标准。 换成 MySQL,WHERE 里会报 Unknown column 'p' in 'where clause'。

别名放在哪 沙箱 MySQL
表别名放 WHERE 能用 能用
字段别名放 WHERE 能用 报错
字段别名放 ORDER BY 能用 能用
字段别名放 GROUP BY 能用 能用

按 MySQL 记就行:表别名哪里都能用,字段别名只能用在 SELECT 之后的子句。 WHERE 在 SELECT 之前,用不了;ORDER BY 在之后,能用。 记不住顺序就记一句话:WHERE 筛的是「行」,那时还没算别名。 这句话和第 9.1 篇讲执行顺序时说的是同一句,别名只是它的一个推论。

六、动手 Demo:两种别名,和它们各自的可见范围

这一段有一组对照特别值得看:同一个别名放在四种位置,沙箱说四个都能用,而 MySQL 只有一个不让。

import sqlite3

c = sqlite3.connect(u"d042.db")

# ① 字段别名:AS 可省,带空格要引号
c.execute(u"SELECT price * 1.1 AS 新价, price * 1.1 \"新 价\" FROM tb_goods").fetchall()

# ② 同一张表两次,别名强制
for sql in [u"SELECT name FROM tb_emp, tb_emp",
            u"SELECT e.name, m.name FROM tb_emp e, tb_emp m",
            u"SELECT tb_emp.name FROM tb_emp e"]:
    try:
        c.execute(sql).fetchall()
        print(u"成功", sql)
    except sqlite3.OperationalError as e:
        print(u"报错", e)

# ③ 别名盖住原列名
c.execute(u"SELECT price AS stock, stock FROM tb_goods ORDER BY stock DESC").fetchall()
c.execute(u"SELECT price AS stock, stock FROM tb_goods ORDER BY 2 DESC").fetchall()

实跑输出:

--------------------------------------------------------------------
第四十二课:两种别名,和它们各自的可见范围
--------------------------------------------------------------------

  **教材 9.7 讲为表和字段取别名。别名的用处是「少打字」和「改显示名」。**
  **但两种别名的可见范围不一样,这一点比语法重要。**

  一、字段别名:AS 可以省,空格要引号
  --------------------------------------------------------------------
  新价 新 价
  ----------
  1.87 1.87
  2.64 2.64
  2 rows in set
  **`AS` 是可以不写的,直接空格加别名也行。**
  **别名里有空格或中文,得用双引号包起来,不然会被当成两个词。**
  **别名只影响结果集的列名,表里的列名一点没变。**
  **所以「改名字」这件事只发生在输出那一层。**

  二、表别名:长名字的救星
  --------------------------------------------------------------------
  id code   name
  --------------
  1  G00001 数码
  2  G00002 家电
  3  G00003 服饰
  3 rows in set
  **表别名一取,整句都能用 `g.` `k.` 这种短前缀。**
  **一旦给表起了别名,原表名在这一句里就不能再用了——这是常被忽略的规矩。**

  三、同一张表出现两次,别名是强制的
  --------------------------------------------------------------------

  写法                结果
  -----------------------------------------------------
  不起别名            报错:ambiguous column name: name
  起了别名            成功
  起了别名又用原表名  报错:no such column: tb_emp.name
  **第一行报 `ambiguous column name: name`——两张同名表并排放着,`name` 是谁的说不清。**
  **这就是自连接必须给两个别名的原因,不是风格问题,是语法要求。**
  **第三行更值得记:起了别名之后原表名就不认了,报 `no such column`。**
  **所以别名不是「再额外加一个名字」,而是「把原来的名字换掉」。**

  四、别名和原列名重名时,谁优先
  --------------------------------------------------------------------
  stock stock
  -----------
  200.9 357
  200.9 357
  200.9 357
  3 rows in set
  stock stock
  -----------
  150.3 499
  100.3 499
  50.3  499
  3 rows in set
  **两句的结果不一样,差别只在 `ORDER BY` 后面写的是名字还是序号。**
  **写 `stock` 排的是别名(也就是 price),写 `2` 排的是真正的第 2 列。**
  **所以「别名盖住原列名」是允许的,而且排在前面的子句里会优先用别名。**
  **这种写法本身就该避免——查错数据时最难查的就是这种「名字一样、意思不同」。**

  五、表别名整个语句都能用,字段别名不一定
  --------------------------------------------------------------------

  别名放在哪             本沙箱的结果
  -----------------------------------
  表别名放 `WHERE`       能用
  字段别名放 `WHERE`     能用
  字段别名放 `ORDER BY`  能用
  字段别名放 `GROUP BY`  能用
  **沙箱里四行都能用,因为 SQLite 对字段别名格外宽松。**
  **换成 MySQL,第二行会报 `Unknown column 'p' in 'where clause'`。**
  **所以这条要按 MySQL 记:表别名哪里都能用,字段别名只能用在 `SELECT` 之后的子句里。**
  **`WHERE` 在 `SELECT` 之前,用不了;`ORDER BY` 在之后,能用。**
  **记不住顺序就记一句话:`WHERE` 筛的是「行」,那时还没算别名。**

自连接那三行是这一课的骨架:报「列名有歧义」、成功、报「没有这一列」。 第三行最容易被忽略——起了别名之后,原表名就不认了。 ORDER BY 那两句只差一个写法,ORDER BY stock 排的是 price, ORDER BY 2 排的是真正的第 2 列。 第一句的三行全是 200.9 / 357,说明它按 price 从大到小取了三行, 而 stock 那列的值一直是 357——两列被完全解耦了。 第二句的三行是 150.3、100.3、50.3,这才是在按 stock 排。 同一个结果集,两个排序写法给出完全不同的顺序,这就是「别名盖住原列名」的后果。

30 秒实验

  1. 把 SELECT price AS stock 的别名改成 p,看 ORDER BY p 和 ORDER BY stock 的区别。
  2. 把 tb_emp e, tb_emp m 的别名去掉,确认它报列名歧义。
  3. 在有别名的句子里写原表名,确认它报 no such column。

常见误区

  1. 给表起了别名又用原表名,报「没有这一列」还以为是拼写错。
  2. 自连接不起别名,报「列名有歧义」。
  3. 用别名覆盖了同名的原列,ORDER BY 排出来不是自己想要的,还不容易发现。
自测三问(点开对答案) 1. 表别名和字段别名的本质区别是什么? —— 表别名是「替换」原表名,起了之后原名不能再用; 字段别名是「追加」,原名还在,只是结果集里多了一个名字。 2. 自连接为什么必须起两个别名? —— 因为同一张表出现两次,引擎无法区分列属于哪一次引用。 不起别名会报列名有歧义,这是语法要求,不是风格问题。 3. MySQL 里字段别名能不能用在 `WHERE` 里? —— 不能,会报 `Unknown column`。因为 `WHERE` 在 `SELECT` 之前执行,那时别名还不存在。

最后一篇 9.8 讲正则查询,包括它在沙箱里怎么找替身。

相关笔记