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 秒实验
- 打开你的库,随便写一条
SELECT,看它属于四类里的哪一类。 如果它改了东西,那它就是 DML,跑之前要想清楚能不能回滚。 - 把
SELECT TOP 5 * FROM 表改成标准的OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY, 两条都能跑,但后者在 MySQL 8 和 Oracle 12c 上也认。 - 想一想:为什么
SELECT要单独分成 DQL? 因为它只读。 只读操作最安全,所以生产库上给人开权限,先开只读——这一条是运维常识。
常见误区
- 「SQL 都一样,学一种就会所有数据库」。语法七成像,剩下三成是坑。 取前 N 行、字符串拼接、日期函数、字符串截取,四家全不一样。所以要记的不是「SQL」, 是「这一家的 SQL」。
- 「标准 SQL 一定更好」。标准保证可移植,但写起来常常更啰嗦。 只跑一种数据库的项目,用方言更清楚。标准是保险,不是教条。
- 「
+拼接和||拼接差不多」。差别很大。 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', 在不同语言设置下是两天。
- ☐ (本篇完,下一篇 6.2 常量)