BEGIN...END 是语句块,不是事务;
事务是 BEGIN TRAN...COMMIT——两对关键字长得像,作用完全不同。
IF 后面只能跟一条语句,要跟多条就必须用 BEGIN...END 包起来,漏了不报错。
教材 6.4 与 6.5 标题几乎重复,内容一分为二:6.4 讲「怎么组合」,6.5 讲「怎么分支」。
这一节的内容在教材里有点特殊,先说清楚它到底讲了什么,再往下看会顺很多。
教材 6.4 的标题是「流程控制语句」,但正文只讲了两样东西:BEGIN...END 语句块、运算符。
真正的 IF、WHILE、CASE、GOTO 全在 6.5「流程控制」里。
所以这一节按「语句怎么组合 + 运算符怎么算」来读,不要按标题去找 IF——找不到的。
一、标题与内容的错位
这是一处编排上的重复,值得先说清楚,免得你翻来翻去找不到。
| 教材节号 | 标题 | 实际内容 |
|---|---|---|
| 6.4 | 流程控制语句 | BEGIN...END、运算符 |
| 6.5 | 流程控制 | IF、CASE、WHILE、RETURN、GOTO |
两个标题只差两个字,内容却一分为二。 按内容看,6.4 讲的是「语句怎么组合成一个整体」, 6.5 讲的是「条件怎么分支、循环怎么写」。 这种编排在老教材里不罕见——可能是两版合并时留下的,也可能是排版时拆节拆错了。 看到标题和内容对不上,先往下翻一节,别怀疑自己看漏了。 本课的安排是:这一节讲 6.4 的实际内容(语句块 + 运算符),下一节讲 6.5 的流程控制。 两节合起来才是完整的「流程控制」。
二、BEGIN...END 是语句块,不是事务
这两对关键字长得像,作用完全不同,而且经常被混:
| 写法 | 是什么 | 作用 |
|---|---|---|
BEGIN...END |
语句块 | 把多条语句当成一条 |
BEGIN TRAN...COMMIT |
事务 | 要么全成功,要么全回滚 |
BEGIN...END 只是「括号」的作用。 它不离事务、不锁表、不回滚——里面某条语句失败了,
前面的照样生效。这一点必须分清。
它存在的唯一理由是「IF 只能跟一条语句」。 想让 IF 管住三条语句,
就得用 BEGIN...END 把它们包成一条。
IF @score >= 60
BEGIN
PRINT '及格'
UPDATE tb SET flag = 1
INSERT INTO log VALUES ('通过')
END
漏写 BEGIN...END 的后果是不报错的。 只有第一条语句受 IF 控制,
后面两条无条件执行——结果就是「有时候对、有时候不对」,最难查的那种。
三、运算符四类
| 类别 | 运算符 | 例子 |
|---|---|---|
| 算术 | + - * / % |
7 / 2 得 3 |
| 比较 | = > < >= <= <> != |
5 <> 3 为真 |
| 逻辑 | AND OR NOT |
真 AND 假 为假 |
| 连接 | + 或 || |
'a' + 'b' 得 'ab' |
第四行是方言差异最大的一处。 SQL Server 用加号拼字符串,标准 SQL 用两条竖线。
所以 '1' + '1' 在 SQL Server 里是 '11',在 SQLite 里是 2。
这个差异下一节(6.7)会真跑对比。 现在只要记住:同一个符号,两边含义可能不同,
而且都不报错。
取模那个 % 也要留意。 它算的是余数,7 % 2 得 1。
用它做「奇偶判断」很方便:WHERE id % 2 = 0 就是取偶数行。
四、优先级:不确定就加括号
| 顺序 | 运算符 |
|---|---|
| 1 | 括号 ( ) |
| 2 | 正负号 + - |
| 3 | 乘除取模 * / % |
| 4 | 加减 + - |
| 5 | 比较 = > < >= <= <> |
| 6 | NOT |
| 7 | AND |
| 8 | OR |
这张表里最容易错的是 6、7、8 三行:NOT 比 AND 先算,AND 比 OR 先算。
NOT 1 = 1 AND 1 = 2 -- 假:(NOT 1=1) AND (1=2)
NOT (1 = 1 AND 1 = 2) -- 真:NOT(真 AND 假)
两行结果相反,就差一对括号。 上面那行是「不是 A,并且 B」, 下面那行是「不是(A 并且 B)」——语义完全不同。 优先级表不用背。 拿不准就加括号——加括号永远不会错,靠记忆会。 而且加了括号,别人读你的 SQL 也快。
五、动手 Demo:语句块与运算符实测
这一段先把两对「长得像」的关键字摆清楚,说明教材 6.4 与 6.5 的标题重复,再列运算符四类和优先级, 最后真跑三条表达式看优先级怎么起作用。
import sqlite3
c = sqlite3.connect(u":memory:")
# 优先级:乘除先于加减
print(c.execute(u"SELECT 1 + 2 * 3").fetchone()[0]) # 7
print(c.execute(u"SELECT (1 + 2) * 3").fetchone()[0]) # 9
# NOT 先于 AND
v = c.execute(u"SELECT NOT 1 = 1 AND 1 = 2").fetchone()[0]
print(u"NOT 1=1 AND 1=2 -> %s" % (u"真" if v else u"假"))
v = c.execute(u"SELECT NOT (1 = 1 AND 1 = 2)").fetchone()[0]
print(u"NOT (1=1 AND 1=2) -> %s" % (u"真" if v else u"假"))
实跑输出:
--------------------------------------------------------------------
BEGIN...END 是语句块,不是事务
--------------------------------------------------------------------
① 两对长得很像的关键字
--------------------------------------------------------------------
| 写法 | 是什么 | 作用 |
|---|---|---|
| BEGIN...END | 语句块 | 把多条语句当成一条 |
| BEGIN TRAN...COMMIT | 事务 | 要么全成功,要么全回滚 |
这两个经常被混。BEGIN...END 只是「括号」的作用,它不离事务、不锁表、不回滚。
教材在 6.5 节讲 BEGIN...END 时配的是 IF 的例子,就是因为它的用途是「配合 IF 用」。
IF 后面只能跟一条语句;要跟多条,就得用 BEGIN...END 包起来。
② 教材 6.4 的标题与内容错位
--------------------------------------------------------------------
教材 6.4 的标题叫「流程控制语句」,但正文只讲了语句块(BEGIN...END)和运算符。
真正的流程控制——IF、WHILE、CASE、GOTO——全在 6.5「流程控制」里。
这是教材编排上的一处重复:6.4 和 6.5 两个标题几乎一样,内容一分为二。
按内容看,6.4 讲的是「语句怎么组合」,6.5 讲的是「条件怎么分支、循环怎么写」。
学的时候按后者理解,不要被两个相似的标题绕住。
③ 运算符:四类
--------------------------------------------------------------------
| 类别 | 运算符 | 例子 |
|---|---|---|
| 算术 | + - * / % | 7 / 2 得 3(整数除法) |
| 比较 | = > < >= <= <> != | 5 <> 3 为真 |
| 逻辑 | AND OR NOT | 真 AND 假 为假 |
| 连接 | + 或 || | 'a' + 'b' 得 'ab' |
连接运算符这一行是方言差异最大的一处:SQL Server 用加号,标准 SQL 用两条竖线。
所以 '1' + '1' 在 SQL Server 里是 '11',在 SQLite 里是 2——一个是拼接,一个是加法。
这个差异下一节(6.7)会真跑对比。
④ 优先级:不确定就加括号
--------------------------------------------------------------------
| 顺序 | 运算符 |
|---|---|
| 1 | 括号 ( ) |
| 2 | 正负号 + - |
| 3 | 乘除取模 * / % |
| 4 | 加减 + - |
| 5 | 比较 = > < >= <= <> |
| 6 | NOT |
| 7 | AND |
| 8 | OR |
1 + 2 * 3 = 7
(1 + 2) * 3 = 9
NOT 1 = 1 AND 1 = 2 = 假
第三行是重点:NOT 比 AND 先算,所以它是 (NOT 1=1) AND (1=2),得假。
如果要表达「不是(A 且 B)」,必须写 NOT (1=1 AND 1=2),括号不能省。
优先级表不用背。拿不准就加括号——加括号不会错,靠记忆会。
第一段两行是同一类:括号改变顺序。 1 + 2 * 3 得 7,(1 + 2) * 3 得 9——这条大家都知道,
但它说明「括号的作用是把优先级短路掉」。
第二段才是重点:两行结果相反,只差一对括号。上面是「不是 A,并且 B」,
下面是「不是(A 并且 B)」。
这两个表达式恰好一个假一个真,看起来像「括号让结果变了」,其实变的是语义,不是优先级。
换个值可能两个都得假——所以别靠试,靠想。
BEGIN...END 那段没法真跑(SQLite 没有这个语法),所以在正文里用代码块贴出来。
它最容易错的地方是「漏写」——而漏写不报错。
30 秒实验
- 写一段
IF 条件后面跟三条语句,不加BEGIN...END,看哪几条真的受条件控制。 你会发现只有第一条受控——这个错不报错,所以只能靠读代码发现。 - 把
NOT A AND B改成NOT (A AND B),用几组不同的真假值试。 试到某一组两个结果不一样,你就理解「语义变了」是什么意思了。 - 想一想:为什么
BEGIN...END会被误认为事务? 因为名字里的 BEGIN 一样。 事务是BEGIN TRAN,语句块是BEGIN——差一个词,差一个量级。
常见误区
- 「
BEGIN...END就是事务」。不是。它只是把多条语句包成一条,某条失败前面的照样生效。 事务是BEGIN TRAN,那个才管回滚。 - 「
IF后面可以跟多条语句」。只能跟一条。 要跟多条必须用BEGIN...END包起来,漏了不报错, 只是后面的语句无条件执行——结果就是「有时候对不上」。 - 「优先级记熟了就不用括号」。记忆会错,括号不会。
而且混用
AND和OR的时候,加括号还能让别人读懂你的意图。这一点比「少打两个字符」重要。
自测三问(点开对答案)
1. `BEGIN...END` 和 `BEGIN TRAN` 有什么区别? —— **前者是语句块,把多条语句当成一条;后者是事务的开始,配合 `COMMIT` 才提交。** **语句块不保证原子性**——里面某条失败,前面的照样生效。 2. `IF` 后面想跟三条语句,怎么办? —— **用 `BEGIN...END` 把三条包起来。** 不包的话只有第一条受 `IF` 控制, 后面两条无条件执行——**而且不报错,这是最麻烦的地方。** 3. `NOT A AND B` 和 `NOT (A AND B)` 有什么区别? —— **前者是「不是 A,并且 B」;后者是「不是(A 并且 B)」。**因为 `NOT` 比 `AND` 先算。 **这两个语义完全不同,靠试值可能试不出差别,必须靠想。**下一篇 6.5 讲真正的流程控制:IF...ELSE、CASE、WHILE、CONTINUE、BREAK、RETURN、
GOTO。其中只有 CASE 是标准 SQL,能真跑;其余都是 T-SQL 的过程化扩展。
- ☐ (本篇完,下一篇 6.5 流程控制)