单行注释 --,多行注释 /* */;两种写法四家数据库都一样,没有方言差异。
/* */ 能不能嵌套看产品:SQL Server 能,其它三家不能——不能的那几家会提前收尾。
GO 不是 SQL 语句,是客户端的分隔符;把它发给数据库会报语法错误。
注释这件事看着没内容,
但 GO 那部分很重要——它是「变量作用域被切断」的原因,也是「程序里带 GO 就报错」的原因。
这两件事都归在「批处理」这个概念下。
教材 6.6 分三小节:单行注释、多行注释、批处理分隔符 GO。
前两节五分钟看完,第三节要慢一点。
演示里跑了一段带两种注释的 SQL(能跑,注释在哪儿都一样),
又试了一次「把 GO 当语句发出去」——报错信息就是最好的证据。
一、两种注释
-- 这是单行注释,一直管到行尾
SELECT 1 AS a -- 行尾也能加
/* 这是多行注释
可以跨好几行
也可以只包一段 */
SELECT 2 AS b
单行注释用两个减号,一直管到这一行结束。 所以它能加在语句末尾,
不影响前面的代码——调试的时候经常用它来「临时注释掉一行」。
多行注释用斜杠星号包起来。 它可以跨行,也可以只包一段——甚至可以插在语句中间:
SELECT /* 中间 */ 1。
注释在四家数据库里写法都一样。 这是本课少有的「完全没有方言差异」的地方——值得庆幸,
因为注释常常是复制粘贴过来的。
一个实用习惯:注释里写「为什么」,不写「是什么」。-- 按 id 排序 这种注释没价值,
代码本身就写着;-- 这里必须按 id 排,否则分页会重复 才有价值。
二、多行注释能不能嵌套
| 数据库 | 能嵌套吗 | 后果 |
|---|---|---|
| SQL Server | 能 | 内外配对,不影响 |
| MySQL | 不能 | 内层结束符就收尾 |
| 标准 SQL | 不能 | 同上 |
| SQLite | 不能 | 同上 |
这一行差异很隐蔽。 SQL Server 里 /* a /* b */ c */ 整段都是注释;
其它三家在第一个 */ 就收尾了,后面的 c */ 就变成了代码。
后果是「报语法错误」或者「多出一段莫名其妙的代码」。如果你注释掉的代码里本来就有 /* */,
这个问题就会出现。
所以两条实用建议: 一是注释里尽量别写 */ 这个符号,换个说法;
二是「注释掉一大段代码」时,用单行注释逐行加,比包一层 /* */ 安全。
三、GO 不是 SQL 语句
这是本节最重要的一句话。 GO 由 SSMS、sqlcmd 这些客户端识别,服务器根本不认识它。
SELECT 1
GO
SELECT 2
客户端看到 GO,就把上面的代码打包成一批发过去,然后发下一批。服务器收到的是两次独立的请求,
它不知道中间有个 GO。
证据很简单:把 GO 当语句发给数据库,会报语法错误。演示里就是这么做的——
报错信息直接说明「数据库不认这个词」。
这解释了两件事: 一是「变量的作用域为什么被切断」(每批独立编译),
二是「为什么程序里带 GO 就报错」(程序是直接发 SQL 的,没有客户端帮你切)。
四、批处理的三个实际影响
| 影响 | 说明 |
|---|---|
| 变量不跨批 | 批 1 声明的变量,批 2 看不见 |
| 编译分开 | 每批独立编译,报错只影响那一批 |
| 某些语句必须独占一批 | CREATE VIEW 后面不能跟别的语句 |
第一条在 6.3 讲过了。 它是「变量未声明」这个报错的根源。
第三条是硬规定,必须记住:CREATE VIEW、CREATE PROCEDURE、CREATE TRIGGER 这类语句,
必须是批里的第一句。
所以脚本里常见 CREATE VIEW 前面有个 GO。 那不是习惯,
是必须——不加的话报「'CREATE VIEW' 必须是查询批中的第一个语句」。
这个报错信息其实说得很清楚,但第一次见的人往往会去找「视图语法哪里错了」,
结果找了半天——其实只是缺一个 GO。
五、动手 Demo:注释与 GO 的真实反馈
这一段跑一段带两种注释的 SQL,试一次嵌套注释,再把 GO 当语句发给数据库——看它报什么,
这是「GO 不是 SQL」最直接的证据。
import sqlite3
c = sqlite3.connect(u":memory:")
sql = u"""-- 单行注释
SELECT 1 AS a -- 行尾注释
/* 多行注释
跨两行 */
, 2 AS b"""
print(c.execute(sql).fetchall())
# 嵌套注释:SQLite 不支持,内层 */ 就收尾了
try:
print(c.execute(u"SELECT /* a /* b */ 1 */ AS x").fetchone())
except sqlite3.OperationalError as e:
print(u"嵌套注释:", e)
# GO 不是 SQL 语句
try:
c.execute(u"SELECT 1 GO")
except sqlite3.OperationalError as e:
print(u"发 GO 出去:", e)
实跑输出:
--------------------------------------------------------------------
注释与 GO
--------------------------------------------------------------------
① 两种注释
--------------------------------------------------------------------
跑一段带两种注释的语句,结果:[(1, 2)]
单行注释用两个减号,一直管到行尾。
多行注释用斜杠星号包起来,可以跨行,也可以只包一段。
两种注释在四家数据库里写法都一样,这一点没有方言差异。
② 多行注释能不能嵌套
--------------------------------------------------------------------
| 数据库 | 能嵌套吗 | 后果 |
|---|---|---|
| SQL Server | 能 | 内外配对,不影响 |
| MySQL | 不能 | 内层结束符就收尾了 |
| 标准 SQL | 不能 | 同上 |
| SQLite | 不能 | 同上 |
嵌套注释试一下:near "/": syntax error
SQL Server 支持嵌套,所以那段能过;其它三家不行——
内层的 */ 一出现,整段注释就结束了,后面的代码就被当成语句。
所以注释里如果要提到 */ 这个符号,干脆别写,换个说法。
③ GO 不是 SQL 语句
--------------------------------------------------------------------
GO 是「批处理分隔符」,由 SSMS、sqlcmd 这些客户端识别,不是服务器认的。
它做三件事:把上面的语句打包成一批发过去、清空变量、重新编译。
这就是证据:数据库不认 GO,只有客户端认。
所以用程序连数据库(JDBC、pymssql 之类)时,SQL 里不能带 GO——会报语法错误。
要么在客户端里跑,要么自己按 GO 切开,一段一段发。
④ 批处理的三个实际影响
--------------------------------------------------------------------
| 影响 | 说明 |
|---|---|
| 变量不跨批 | 批 1 声明的变量,批 2 看不见 |
| 编译分开 | 每批独立编译,报错只影响那一批 |
| 某些语句必须独占一批 | CREATE VIEW 后面不能跟别的语句 |
第三条是硬规定:CREATE VIEW、CREATE PROCEDURE 这类语句必须是批里的第一句。
所以脚本里常见 CREATE VIEW 前面有个 GO——不是习惯,是必须。
这一条教材在视图那一章会再说一次,先记住「建视图要单独一批」。
第一段能跑,说明注释不影响语义。 两种注释混在同一段 SQL 里,
结果还是 (1, 2)——注释被解析器直接跳过了。
第二段的报错就是「嵌套不了」的证据。 内层的 */ 一出现,整段注释就结束了,
后面的 1 */ 变成了代码——所以报语法错误。
第三段是本节的核心证据:GO 发给数据库,报「语法错误」。说明它不是 SQL 语句,只有客户端认它。
这两条加起来解释了一个常见困惑: 在 SSMS 里跑得好好的脚本,用程序(JDBC、
Python)跑就报错——因为程序没有客户端那层,不会替你切 GO。
30 秒实验
- 在 SSMS 里跑一段带
GO的脚本,再把同样的内容通过程序发出去。 前者成功,后者报语法错误——这就是「客户端 vs 服务器」的分界线。 - 写一个
CREATE VIEW脚本,前面不加GO,看它报什么。 报的是「必须是查询批中的第一个语句」——加上GO就好了。 - 想一想:为什么注释掉的代码里如果有
*/,会出问题? 因为多行注释在第一个*/就收尾了。 后面的内容变成了代码,不报错才怪。
常见误区
- 「
GO是一条 SQL 语句」。不是。它是客户端的分隔符,服务器不认识。 把它发给数据库会报语法错误——程序里带GO就是这个原因。 - 「多行注释可以随便嵌套」。只有 SQL Server 能嵌套。
其它三家在第一个
*/就收尾,后面的内容变成代码。注释掉带/* */的代码时尤其要小心。 - 「注释越多越好」。注释写「是什么」是噪音,写「为什么」才有价值。 代码本身写着「按 id 排序」,你再注释一遍没有信息量;写清「为什么必须按 id 排」才有用。
自测三问(点开对答案)
1. `GO` 是 SQL 语句吗? —— **不是,是客户端的分隔符。** SSMS、`sqlcmd` 认它,服务器不认。 **所以程序里带 `GO` 会报语法错误**——程序是直接发 SQL 的,没有客户端帮你切。 2. 为什么 `CREATE VIEW` 前面要加 `GO`? —— **因为这类语句必须是批里的第一句。** `CREATE VIEW`、`CREATE PROCEDURE`、 `CREATE TRIGGER` 都受这个限制——**不加 `GO` 就报「必须是查询批中的第一个语句」。** 3. 多行注释嵌套会出什么问题? —— **除 SQL Server 外,其它三家在第一个 `*/` 就收尾了。** 后面的内容变成代码, 报语法错误或者多出一段莫名其妙的语句。**注释里尽量别写 `*/` 这个符号。**下一篇 6.7 讲运算符。重点是两处不报错的方言差异:+ 是加法还是拼接、
整数除法为什么丢掉小数——这两个都是「结果不对但不报错」的典型。
- ☐ (本篇完,下一篇 6.7 运算符)