🦏 第2章 SQL语法
📄 一句话概括

SQL 是跟数据库说话的语言,一句话分五类:DDL 动结构、DML 动数据、DQL 只读、DCL 动权限、 TCL 动事务。 本书用到前四类,第五类只在第 15 章露一次面。 Demo 三类各跑一句; 然后撞两次书写规矩:CREATE TABLE order 报 near "order": syntax error,加反引号才过; SELECT "abc" 在这边给 abc,但它靠的是「找不到列就退化成字符串」这条旧规矩, 不是「双引号表示字符串」。

📝 本章和前面几章的关系

上一篇把数据库装好了,这一篇开始说它听得懂的话。 语法这块只有三条硬规矩(分号、引号、反引号),其余都是习惯。 它也是全书的语法公约,后面每一篇都按这里定的规矩写。

一、SQL 是语言,不是「MySQL 的操作命令」

SQL 全称 Structured Query Language,1974 年在 IBM 提出,后来成了 ANSI/ISO 标准。 MySQL、PostgreSQL、SQL Server、Oracle 都实现它。 同一个意思每家都有方言:分页 MySQL 写 LIMIT、SQL Server 写 TOP、Oracle 8i 得套 ROWNUM。 所以「会 SQL」的意思是「会标准那部分,再记住目标产品的方言」。 本书只用 MySQL 方言,但凡是标准写法能过的,都尽量用标准写法——这样换库时少改几行。

二、五类语句:先记住它们管什么

DDL(数据定义):CREATE/ALTER/DROP/TRUNCATE——动表结构。 这一类要格外小心,很多是没法回滚的。 DML(数据操纵):INSERT/UPDATE/DELETE——动表里的行。 DQL(数据查询):SELECT——只读不写。 标准里把它归在 DML,习惯上单列,因为九成的活都在这。 DCL(数据控制):GRANT/REVOKE——动权限,第 16 章讲。 TCL(事务控制):COMMIT/ROLLBACK/SAVEPOINT——第 15 章讲。

三、三条硬规矩:分号、单引号、反引号

一条语句以分号结尾(客户端靠它断句)。 字符串用单引号,里面的单引号写两个:'it''s'。 标识符(表名、字段名)撞上关键字时,用反引号包起来:`order`。 反引号是 MySQL 的写法,标准 SQL 用双引号,但 MySQL 默认把双引号当字符串, 所以两边会打架——统一用反引号最省事。 双引号在这边也能给出字符串,但那是因为「找不到叫 abc 的列,就退化成字符串字面量」, 属于历史遗留。 一旦开启 ANSI_QUOTES,这条退路就没了,同一句立刻报错。 别依赖它:字符串一律单引号。

四、命名规则:四条硬要求,两条软习惯

硬要求:字母或下划线开头;只用字母、数字、下划线;不许是保留字;长度别超 64 个字符。 软习惯:全小写加下划线(order_detail、student_id); 表名用单数还是复数,全库统一——最怕一半 user 一半 users。 三个别做:用中文当表名(能跑,但到处要加引号)、用 user/order/desc/key 当名字、 靠大小写区分两张表。

五、小结

教材小结:SQL 是操作关系型数据库的标准语言,按功能分 DDL、DML、DQL、DCL、TCL 几类; 语句以分号结尾,关键字不区分大小写,标识符有命名规则。 本课补三条:一是方言差异集中在分页、日期函数、字符串拼接这三处; 二是标识符撞关键字就加反引号,字符串一律单引号;三是命名全小写加下划线,别拿大小写当区分。

六、动手 Demo:三类语句各一句,再撞两条书写规矩

Demo 六段:先列五类语句,再真跑 DDL/DML/DQL 各一句, 然后试「关键字当表名」「双引号」「单引号套单引号」,最后讲命名规则。 两处报错都是真撞的——near "order": syntax error 和我预想的一样, SELECT "abc" 却出乎意料地没报错,这一点在正文里照实说明。

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

# DDL:动结构
c.execute("CREATE TABLE t_ddl (id INTEGER PRIMARY KEY, "
          "name TEXT, price REAL)")
# DML:动数据
c.execute("INSERT INTO t_ddl (name, price) VALUES ('篮球', 128.00), "
          "('足球', 156.00)")
# DQL:只读
c.execute("SELECT id, name, price FROM t_ddl WHERE price >= 150")

# 关键字当表名:不加反引号报错,加了才过
c.execute("CREATE TABLE order (id INTEGER)")
c.execute("CREATE TABLE `order` (id INTEGER)")

# 双引号在这边的真实行为
c.execute("SELECT \"abc\"")
# 单引号里的单引号写两个
c.execute("SELECT 'it''s ok'")

实跑输出:

--------------------------------------------------------------------
第六课:SQL 的五类语句与三条书写规矩
--------------------------------------------------------------------

  一句 SQL 分五类,本书用到四类
  DDL(定义):create/alter/drop/truncate —— 动结构
  DML(操纵):insert/update/delete —— 动数据
  DQL(查询):select —— 只读不写(标准里归在 DML,习惯上单列)
  DCL(控制):grant/revoke —— 动权限
  TCL(事务):commit/rollback/savepoint —— 动事务

  真跑三类各一句
  SQL> CREATE TABLE t_ddl (id INTEGER PRIMARY KEY, name TEXT, price REAL)
  完成
  SQL> INSERT INTO t_ddl (name, price) VALUES ('篮球', 128.00), ('足球', 156.00)
  影响 2 行
  SQL> SELECT id, name, price FROM t_ddl WHERE price >= 150
  id     name     price
  -----------------------
  2      足球     156.00
  (1 行)

  **结构、数据、查询三件事各归各的语句,这是 SQL 最外层的一张地图。**

  关键字不能直接当表名,要加反引号
  SQL> CREATE TABLE order (id INTEGER)
  报错:near "order": syntax error
  SQL> CREATE TABLE `order` (id INTEGER)
  完成  ← 反引号把它标成「标识符」,不是关键字
  **反引号是 MySQL 的写法,SQLite 也兼容。**
  **标准 SQL 用双引号(`"order"`),但 MySQL 默认把双引号当字符串,两边会打架。**

  双引号:两边现在都给字符串,但这是「将就」
  SELECT "abc"  -> 'abc'
  **这句没报错,给的就是字符串 abc。** 但它不是「双引号表示字符串」的意思,
  **而是「找不到叫 abc 的列,就退化成字符串」。**
  **MySQL 默认模式下的结果一样,原因却不同:它真把双引号当字符串。**

  **两边现在都给 abc,但一旦开启 `ANSI_QUOTES`(MySQL)或严格模式,**
  **双引号就变成「标识符」,同一句立刻报找不到列。**
  **所以结论只有一条:字符串一律用单引号,双引号留给标准 SQL 的标识符,别混用。**

  单引号里的单引号,写两个
  SQL> SELECT 'it''s ok' AS s1, 'He said ''hi''' AS s2
  s1          s2
  -------------------------
  it's ok     He said 'hi'
  (1 行)

  **两个单引号表示一个单引号**,这是 SQL 的标准写法,不用反斜杠(反斜杠是 MySQL 的扩展,
  换个库就不认)。

  命名规则:四条硬要求,两条软习惯
  **硬要求**:字母或下划线开头;只能用字母、数字、下划线;不许用保留字;长度别超 64。
  **软习惯**:全小写加下划线(`order_detail`);表名用单数还是复数,全库统一就行。
  **别做的事**:用中文当表名(能跑但到处要加引号)、用 `user`/`order`/`desc` 当名字、
  靠大小写区分两张表。

第一段列五类。 定义、操纵、查询、控制、事务, 各管一件事,混起来就会写出「用 DDL 去改数据」这种怪东西。 第二段真跑三类。 price >= 150 只捞出足球一行,结构、数据、查询三件事在三句里各归各位。 第三段关键字当表名。 CREATE TABLE order 直接报 near "order": syntax error, 加反引号就过——反引号的意思是「这是名字,不是关键字」。 第四段双引号。 SELECT "abc" 给了 abc,没报错; 原因是「找不到列就退化成字符串」,不是「双引号表示字符串」。 这条退路在 ANSI_QUOTES 下没有,所以别依赖。 第五段单引号套单引号。 'it''s ok' 输出 it's ok,两个单引号表示一个,这是标准写法, 比反斜杠可移植。 第六段命名规则。 四条硬要求、两条软习惯、三个别做。

30 秒实验

  1. 把 price >= 150 改成 price >= 128,命中几行?
  2. (先想再跑)CREATE TABLE "order" (id INTEGER) 在这边能过吗?在 MySQL 默认模式下呢?
  3. SELECT '它''s' 和 SELECT '它\'s' 在这边结果一样吗?

常见误区

  1. 用双引号写字符串。 MySQL 默认能过,但开了 ANSI_QUOTES 就报错; SQLite 那边更是靠「退化」这条旧规矩撑着。
  2. 表名直接叫 order/user/desc。 全是保留字,不加反引号就语法错, 而且这种错只在某些语句上出现,很难查。
  3. 靠大小写区分表名。 Windows 上能跑、Linux 上找不到表,这类 bug 只在部署那天爆。
自测三问(点开对答案) 1. SQL 的五类语句各管什么? —— DDL 管结构(create/alter/drop/truncate)、DML 管数据(insert/update/delete)、 DQL 管查询(select)、DCL 管权限(grant/revoke)、TCL 管事务(commit/rollback)。 2. 标识符撞上关键字怎么办? —— 用反引号包起来(MySQL 写法);标准 SQL 用双引号,但 MySQL 默认把双引号当字符串。 3. 为什么字符串要用单引号? —— 单引号是标准写法,两边都认; 双引号在 MySQL 默认是字符串、开了 ANSI_QUOTES 就变标识符,行为随配置变,不能依赖。

语法骨架有了,接下来是「每个字段该用什么类型」—— 这一篇是本课与 MySQL 差异最大的地方。

相关笔记