🪟 第6章 SQL基础
📄 一句话概括

SQL 按「改什么」分四类:DDL 改结构、DML 改数据、DQL 只读、DCL 管权限。 分类的意义在「能不能回滚」——DDL 多数不能,DML 能。 T-SQL 不是标准 SQL:取前 10 行、字符串拼接、当前时间,四家写法全不一样。

📝 本章和前面几章的关系

前五章都在讲「怎么把库建起来」,从这一章开始讲「怎么跟它说话」。这一节是概览, 把 SQL 分成几块、把 SQL Server 的方言跟标准 SQL 的差别摆出来。 教材 6.1 分四小节:简介、特点、组成、T-SQL 与标准 SQL。前三节是概念,第四节才是要记住的。 演示里列了五行「同一件事、四家怎么写」,只有 SQLite 那一列能真跑——这不是偷懒, 是本沙箱没有 SQL Server。凡是方言差异,正文都会写清。

一、四类语句,按「改什么」分

类别 全称 管什么 代表语句
DDL 数据定义语言 表、库的结构 CREATE / ALTER / DROP
DML 数据操作语言 表里的行 INSERT / UPDATE / DELETE
DQL 数据查询语言 只读,不改数据 SELECT
DCL 数据控制语言 权限 GRANT / REVOKE

这么分类的意义只有一个:能不能回滚。 DML 改的是行,每一行都记日志,所以能回滚; DDL 改的是结构,很多操作不能回滚——DROP TABLE 之后没有「撤销」。 DQL 单独拎出来,因为 SELECT 用得最多。 严格说它属于 DML,但它不改任何东西, 只读操作最安全——所以生产库上给人开权限,先给只读,这是最稳的做法。 还有一类叫 TCL(事务控制语言):BEGIN TRAN、COMMIT、ROLLBACK。教材没有单独列, 但它是 DML 能回滚的前提,天天用。 分类不是为了背,是为了判断风险。 看到 DROP、TRUNCATE、ALTER 就多看一眼, 看到 SELECT 就放心跑——这个直觉比记住四类的全称有用。

二、同一件事,四家写法不一样

这是本节最该看的一张表。五行,五个最常见的操作,四种数据库四种写法:

要做什么 SQL Server MySQL Oracle
取前 10 行 TOP 10 LIMIT 10 ROWNUM <= 10
字符串拼接 a + b CONCAT(a,b) a || b
当前时间 GETDATE() NOW() SYSDATE
类型转换 CAST(x AS INT) CAST(x AS SIGNED) TO_NUMBER(x)
空值替换 ISNULL(x,0) IFNULL(x,0) NVL(x,0)

第一行最典型。 分页这件事,标准 SQL 直到 2008 才定下来(OFFSET ... FETCH), 在那之前各家自己造:SQL Server 用 TOP、MySQL 用 LIMIT、Oracle 用 ROWNUM。 第二行的加号是个陷阱。 SQL Server 的 a + b 在两边都是字符串时是拼接, 但在 SQLite 里是算术加法——'1' + '1' 一个得 '11'、一个得 2。而且不报错。 第五行是「同一个功能三个名字」的典型。 ISNULL、IFNULL、NVL 干的是一件事: 遇到 NULL 就换成另一个值。 标准 SQL 里对应的是 COALESCE,四家都认——所以写 COALESCE 是最省心的,它跨库。

三、标准 SQL 与 T-SQL 的边界

标准 SQL 是 ISO 定的,T-SQL 是微软在标准上加的扩展。 扩展分两种:一种是「标准没有, 我补上」(比如 TOP、GETDATE()),一种是「标准有,我换个写法」(比如用 + 代替 ||)。 写 SQL 时的一个实用原则:能用标准的就用标准的。CAST 而不是 CONVERT、 COALESCE 而不是 ISNULL、CURRENT_TIMESTAMP 而不是 GETDATE()——这些写法 SQL Server 全认, 而且换库的时候不用改。 但也别走极端。 TOP 10 比 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 短得多, 在只跑 SQL Server 的项目里,用 TOP 更清楚。标准是为了可移植,不是为了好看。 本课的做法是:教材用 T-SQL,正文跟着用,遇到跨库会不一样的地方标出来。 你以后大概率会同时碰到 SQL Server 和 MySQL,知道哪里不一样,比背「标准答案」有用。

四、本沙箱能跑的是哪一列

演示里那五行,只有 SQLite 那一列真跑了:

a || b        = 订单A001
CAST(x AS INT)= 123
IFNULL(NULL,0)= 0
datetime now  = 2026-10-31 ...

四行里有三行是标准写法(||、CAST、IFNULL 在 SQLite 里就是这么写), 只有 datetime('now') 是 SQLite 专有的。 所以 SQLite 更接近「标准 SQL」,SQL Server 的方言更多。这不是谁好谁坏—— SQL Server 的方言是为了方便,代价是可移植性差一点。 记住这一节的结论:语法差异不可怕,可怕的是「不报错的差异」。TOP 写成 LIMIT 会直接报错, 一眼能改;+ 和 || 混用不报错,只是结果不对——后者才是迁移时真正花时间的地方。

五、动手 Demo:四类语句与四家方言

这一段先把 DDL、DML、DQL、DCL 四类列清楚,再把五个最常见操作的四种写法并排摆出来, 最后只跑 SQLite 那一列,看它能跑出什么。

import sqlite3

c = sqlite3.connect(u":memory:")
c.execute(u"CREATE TABLE t (a TEXT, b TEXT, x TEXT)")
c.execute(u"INSERT INTO t VALUES ('订单', 'A001', '123')")

q = u"""SELECT a || b, CAST(x AS INT), IFNULL(NULL, 0),
               datetime('now') FROM t"""
r = c.execute(q).fetchone()
print(u"a || b         = %s" % r[0])
print(u"CAST(x AS INT) = %d" % r[1])
print(u"IFNULL(NULL,0) = %d" % r[2])
print(u"datetime now   = %s" % r[3])

实跑输出:

--------------------------------------------------------------------
SQL 的四类与四家的方言
--------------------------------------------------------------------


  ① 四类语句,按「改什么」分
  --------------------------------------------------------------------
| 类别 | 全称 | 管什么 | 代表语句 |
|---|---|---|---|
| DDL | 数据定义语言 | 表、库的结构 | CREATE / ALTER / DROP |
| DML | 数据操作语言 | 表里的行 | INSERT / UPDATE / DELETE |
| DQL | 数据查询语言 | 只读,不改数据 | SELECT |
| DCL | 数据控制语言 | 权限 | GRANT / REVOKE |

  分类的意义在「能不能回滚」。DDL 改结构,多数情况下不能回滚;DML 改数据,能。
  DQL 单独拎出来,是因为 SELECT 用得最多,而且它不改任何东西——只读操作最安全。
  还有一类叫 TCL(事务控制语言):BEGIN TRAN / COMMIT / ROLLBACK。教材没单独列,但天天用。

  ② 同一个功能,四家写法不一样
  --------------------------------------------------------------------
| 要做什么 | SQL Server | MySQL | Oracle | SQLite |
|---|---|---|---|---|
| 取前 10 行 | SELECT TOP 10 * | LIMIT 10 | ROWNUM <= 10 | LIMIT 10 |
| 字符串拼接 | a + b | CONCAT(a,b) | a || b | a || b |
| 当前时间 | GETDATE() | NOW() | SYSDATE | datetime('now') |
| 类型转换 | CAST(x AS INT) | CAST(x AS SIGNED) | TO_NUMBER(x) | CAST(x AS INT) |
| 空值替换 | ISNULL(x,0) | IFNULL(x,0) | NVL(x,0) | IFNULL(x,0) |

  这五行的差异,是「T-SQL 不是标准 SQL」最直观的证据。
  标准 SQL 只规定了 CAST、|| 这些;各家为了性能或者历史包袱,各加了一套。

  ③ 本沙箱能跑的是哪一列
  --------------------------------------------------------------------
  a || b        = 订单A001
  CAST(x AS INT)= 123
  IFNULL(NULL,0)= 0
  datetime now  = 2026-10-30 18:47:09
  只有最后一列(SQLite)能跑。前三列都是「教材原码,本沙箱不执行」。
  所以本课凡是遇到方言差异,正文都会把四家写法并排列出来,实跑只跑 SQLite 那一列。

这一段跑出来的四行,三行是标准写法。 || 拼接、CAST 转换、IFNULL 空值替换, SQL Server 只认第一个和第二个,IFNULL 要换成 ISNULL。 最后那行 datetime('now') 是 SQLite 专有的,而且取的是 UTC 时间。 要本地时间得写 datetime('now','localtime')——这个差别会让「今天的订单」查出昨天的数据。 所以这一节的重点不是「记住四类名字」,是「知道哪里会不一样」。五行对比表里, 前三行差异明显(会报错),后两行差异隐蔽(不报错但结果不同)。 隐蔽的那种才要命。 迁移的时候,报错的十分钟能改完,不报错的可能要查三天。

30 秒实验

  1. 打开你的库,随便写一条 SELECT,看它属于四类里的哪一类。 如果它改了东西,那它就是 DML,跑之前要想清楚能不能回滚。
  2. 把 SELECT TOP 5 * FROM 表 改成标准的 OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY, 两条都能跑,但后者在 MySQL 8 和 Oracle 12c 上也认。
  3. 想一想:为什么 SELECT 要单独分成 DQL? 因为它只读。 只读操作最安全,所以生产库上给人开权限,先开只读——这一条是运维常识。

常见误区

  1. 「SQL 都一样,学一种就会所有数据库」。语法七成像,剩下三成是坑。 取前 N 行、字符串拼接、日期函数、字符串截取,四家全不一样。所以要记的不是「SQL」, 是「这一家的 SQL」。
  2. 「标准 SQL 一定更好」。标准保证可移植,但写起来常常更啰嗦。 只跑一种数据库的项目,用方言更清楚。标准是保险,不是教条。
  3. 「+ 拼接和 || 拼接差不多」。差别很大。 SQLite 里 + 是加法,'1' + '1' 得 2;SQL Server 里是拼接,得 '11'。而且两边都不报错—— 这种差异最难查。
自测三问(点开对答案) 1. DDL 和 DML 最关键的区别是什么? —— **能不能回滚。** DML 改的是行,每行都记日志,能回滚; DDL 改的是结构,`DROP TABLE` 之后没有撤销。**所以看到 DDL 语句要多看一眼。** 2. `ISNULL`、`IFNULL`、`NVL` 有什么共同点? —— **都是「遇到 NULL 换成另一个值」,只是三家的名字不同。**标准写法是 `COALESCE`, 四家都认——**写 `COALESCE` 最省心,跨库不用改。** 3. 为什么说「不报错的语法差异最可怕」? —— **因为报错的十分钟能改完,不报错的要查三天。**`TOP` 写成 `LIMIT` 会直接报错; `+` 和 `||` 混用不报错,只是结果不对——而结果不对,你首先会怀疑数据,不会怀疑语法。

下一篇 6.2 讲常量。 数字、字符串、日期三种字面量各有各的写法,其中日期最麻烦——同一个 '10/11/2026', 在不同语言设置下是两天。

相关笔记