🪟 第4章 创建与管理数据库
📄 一句话概括

常规标识符四条硬规则:首字符只能是字母、下划线、at 号、井号;后续可加数字和美元符号; 不能是保留字;不超过 128 字符。 @ 是变量、@@ 是系统函数、# 是局部临时表、## 是全局临时表——前缀不是装饰。 名字里要有空格或保留字,套一层方括号就行;但每次写 SQL 都得套,忘一次就报错。

📝 本章和前面几章的关系

这一节教材讲得很细,四条规则、三种命名风格、四段式对象名。 看着琐碎,但它是「语法错误」最常见的来源。 真正要带走的是两件事: 一是哪些名字不用引号就能用, 二是「四段式名字」怎么读——后者在跨库查询和链接服务器里天天见。 演示里写了个校验器,把一批候选名挨个判一遍, 再让 SQLite 试着用保留字当列名——它跟 SQL Server 一样认方括号,所以这段能真跑。

一、常规标识符的四条硬规则

所谓「常规标识符」,就是不用加任何括号就能直接写的名字。它有四条硬规则:

1. 首字符:字母、下划线 _、at 号 @、井号 #
2. 后续字符:字母、数字、下划线、at 号、井号、美元符号
3. 不能是 T-SQL 的保留字
4. 长度不超过 128 个字符

这四条里,第一条最容易被忽略。 数字不能开头——2024tb 不行,tb2024 可以。 中文可以开头,因为 SQL Server 的标识符允许 Unicode 字母,「学生表」是个合法名字。 第二条里那个美元符号只准出现在中间和后面。 所以 tb$1 合法, $tb 不合法——这也是为什么命名实例的服务名是 MSSQL$SQLEXPRESS:美元符号在中间,合规。 第三条是坑最多的一条。 select、from、order、user、key、index 都是保留字, 直接拿来当列名会报语法错误。解决办法不是改名,是加方括号——下一节细说。

二、前缀不是装饰,是分类

前缀 含义 活在哪儿
@ 局部变量 当前批处理,出了就没了
@@ 系统函数 全局,如 @@VERSION
# 局部临时表 当前连接可见,断开就删
## 全局临时表 所有连接可见

这四个前缀是语法的一部分,不是命名习惯。 看到 @name,编译器就当变量处理,不会去表里找。 所以给表起名叫 @tb 是自找麻烦——能建,但后面查它的时候名字被当成变量了。 @@ 开头的是系统函数,不能自己定义。 你能写 @@VERSION、@@ROWCOUNT, 但不能写 @@MINE——那会报「名称不是可识别的选项」。 # 和 ## 的区别是作用域。 #temp 只有当前连接看得到,连接一断表就没了; ##temp 所有连接都能看到,最后一个用它的人断开之后才消失。 临时表放在 tempdb 里,不是放在你的库。 所以它们不占你的数据文件,也不会被备份——这两点经常有人问「临时表的数据能不能恢复」,答案是不能。

三、保留字当列名:加方括号

方括号是 SQL Server 的「分隔标识符」。 它的意思是「这串字符就是个名字,别当关键字解释」。 演示里让 SQLite 试了一次:不加括号建 CREATE TABLE t1 (select TEXT) 直接报语法错误; 加上括号 CREATE TABLE t1 ([select] TEXT, [order] INT) 就建成了,也能正常查。 四种数据库的分隔符不一样,这是跨库迁移时的第一个坑:

数据库 分隔符 例子
SQL Server 方括号 [select]
SQLite 方括号或双引号 [select] 或 select
MySQL 反引号 select
Oracle 双引号 select

不推荐拿保留字当列名。 虽然加括号能用,但从此每一条 SQL 都得加——自己写的记得,ORM 生成的、 报表工具拼的、别人写的不一定记得。一次忘记就报错,而且报得莫名其妙。 真遇到别人设计的库用了保留字,记住一句话:加方括号。 这比改表名安全得多, 改表名要动所有引用它的 SQL、视图、存储过程。

四、四段式对象名怎么读

完整写法是四段,用点隔开:服务器.数据库.架构.对象。举个例子:

SRV01.shop.dbo.tb_goods
  |     |    |     |
  |     |    |     +-- 表名
  |     |    +-------- 架构(schema)
  |     +------------- 数据库
  +------------------- 服务器(链接服务器才有意义)

四段可以省略,从右往左补默认值。 只写 tb_goods,就是「本机、当前库、dbo、tb_goods」。 写两段 shop.dbo.tb_goods,就是「本机、shop 库、dbo、tb_goods」。 dbo 是架构名,不是用户名。 它默认是数据库所有者,所以绝大多数表都在 dbo 下面。 SQL Server 2005 之前没有架构这一层,这就是老教材里看不到四段名的原因。 写全四段的好处是「换台机器也能跑」。 跨库查询、链接服务器、 同一段脚本在测试库和生产库之间切换,都靠这个。 但写全了迁移时又得改——所以常见的做法是用 USE 库名 切换上下文,SQL 里只写两段。

五、动手 Demo:校验一批候选名,再试保留字

这一段按四条规则写个校验器,把一批候选名挨个判一遍; 再列出四个前缀各自的含义,最后让 SQLite 试一次「保留字当列名」——加方括号和不加,结果完全不同。

import sqlite3

def ok_first(s):
    u"""首字符:字母、下划线、at 号、井号"""
    return bool(s) and (s[0].isalpha() or s[0] in u"_@#")

def ok_rest(s):
    u"""后续:字母、数字、下划线、at 号、井号、美元符号"""
    return all(c.isalnum() or c in u"_@#$" for c in s[1:])

RESERVED = {u"select", u"from", u"order", u"user", u"key", u"index"}

for s in (u"tb_student", u"#temp1", u"学生表", u"2024tb",
          u"tb-student", u"select"):
    print(u"%-12s 首 %-3s 后续 %-3s"
          % (s, ok_first(s), ok_rest(s)),
          u"能" if (ok_first(s) and ok_rest(s)
                     and s not in RESERVED) else u"不能")

# 保留字当列名,加不加方括号
c = sqlite3.connect(u":memory:")
try:
    c.execute(u"CREATE TABLE t1 (select TEXT)")
except sqlite3.OperationalError as e:
    print(u"不加括号:", e)
c.execute(u"CREATE TABLE t1 ([select] TEXT, [order] INT)")
print(u"加方括号:建表成功")

实跑输出:

--------------------------------------------------------------------
命名规则:哪些名字能过
--------------------------------------------------------------------


  ① 常规标识符的四条硬规则
  --------------------------------------------------------------------
  1. 首字符:字母、下划线 _、at 号 @、井号 #
  2. 后续字符:字母、数字、下划线、at 号、井号、美元符号
  3. 不能是 T-SQL 的保留字
  4. 长度不超过 128 个字符

| 候选名 | 首字符合法 | 后续合法 | 判断 |
|---|---|---|---|
| tb_student | 是 | 是 | 能 |
| _tmp | 是 | 是 | 能 |
| #temp1 | 是 | 是 | 能 |
| @变量 | 是 | 是 | 能 |
| 学生表 | 是 | 是 | 能 |
| tb2024 | 是 | 是 | 能 |
| 2024tb | 不是 | 是 | 不能 |
| tb-student | 是 | 不是 | 不能 |
| tb student | 是 | 不是 | 不能 |
| select | 是 | 是 | 不能 |
| tb!x | 是 | 不是 | 不能 |

  注意中间那几个中文名:「学生表」是合法标识符。
  SQL Server 的标识符允许 Unicode 字母,中文、日文都能直接用,不用加引号。
  但工程上一般还是用英文加下划线——换编码、换工具的时候少一层麻烦。

  ② 头和尾那两个符号的意思不一样
  --------------------------------------------------------------------
| 前缀 | 含义 | 活在哪儿 |
|---|---|---|
| @ | 局部变量 | 当前批处理里,出了就没了 |
| @@ | 系统函数 | 全局,如 @@VERSION、@@ROWCOUNT |
| # | 局部临时表 | 当前连接可见,断开就删 |
| ## | 全局临时表 | 所有连接可见,最后一个引用断开才删 |

  所以 @ 开头的名字是变量,不是表。给表起名叫 @tb 是不行的——
  能过语法检查,但你后面 select 它的时候,它被当成变量了。

  ③ 保留字当列名:加方括号就能过
  --------------------------------------------------------------------
  不加括号:CREATE TABLE t1 (select TEXT)
    near "select": syntax error
  加方括号:CREATE TABLE t1 ([select] TEXT, [order] INT)
    建表成功,插了一行,查出来是:用方括号过了
  方括号是 SQL Server 的「分隔标识符」,意思是「这串字符就是个名字,别当关键字解释」。
  SQLite 也认方括号,这一点两边一样;MySQL 用反引号,Oracle 用双引号。
  不推荐拿保留字当列名——虽然加括号能用,但每次写 SQL 都得加,忘一次就报错。

  ④ 四段式对象名
  --------------------------------------------------------------------
  完整写法:服务器.数据库.架构.对象
  例:SRV01.shop.dbo.tb_goods
  四段可以省略,从右往左补默认值:只写 tb_goods,就是「本机、当前库、dbo、tb_goods」。
  写全四段的好处是「换台机器也能跑」——跨库查询、链接服务器都靠这个。
  dbo 是架构名,不是用户名。它默认是数据库所有者,所以绝大多数表都在 dbo 下面。
  SQL Server 2005 之前没有架构这一层,这是老教材里看不到四段名的原因。

第一段那个校验器只有二十来行,但把四条规则里的前三条都覆盖了。 第四条(128 字符上限)没写——实际用不到那么长的名字,写进去反而看不清重点。 学生表 判出来是「能」,这跟很多人直觉不一样。 SQL Server 的标识符允许 Unicode 字母, 中文名直接能建,不用加引号。但工程上还是用英文,换工具、换编码的时候少一层麻烦。 tb-student 和 tb student 都是「首字符合法、后续不合法」。这种名字不是不能建—— 加方括号就行,但加了之后它就是「分隔标识符」了,每次引用都得加。 第二段是本节的答案。 不加括号报 near "select": syntax error, 加了就过——因为方括号把「关键字」变成了「名字」。 SQLite 认方括号,所以这段能在本地真跑,跟 SQL Server 的行为一致。

30 秒实验

  1. 用你自己的名字拼音建一张表,比如 zhang_san_test,再试一次 zhang-san。 第二次会报错,报的是 near "san": syntax error——横线被当成了减号。
  2. 在 SSMS 里写 SELECT * FROM [select],看它报什么错。 这一行同时演示了两件事:方括号怎么用,以及「名字能过语法但表不存在」是两个不同的错。
  3. 想一想:为什么 MSSQL$SQLEXPRESS 这个名字是合法的? 因为美元符号只能出现在中间和后面,不能开头。 这条规则顺带解释了命名实例的写法。

常见误区

  1. 「名字随便起,能跑就行」。能跑是暂时的。 带空格、带横线、是保留字的名字,每写一次 SQL 就要加一次括号,换个工具、换个 ORM 就可能漏。 命名规则存在的意义是「少一次出错的机会」。
  2. 「中文列名不好,必须英文」。技术上没问题,SQL Server 原生支持。 真正的问题在工具链——某些导出工具、某些驱动、某些老版本的客户端对非 ASCII 列名支持不好。 所以是「工程习惯」,不是「技术要求」。
  3. 「四段名写得越全越专业」。写全了换环境就得改。 日常用 USE 切库 + 两段名(dbo.tb),最省事。四段名留给跨库查询和链接服务器。
自测三问(点开对答案) 1. `2024tb` 和 `tb2024` 哪个是合法标识符? —— **`tb2024` 合法,`2024tb` 不合法。** 标识符不能以数字开头—— 因为数字开头的会被当成数字字面量解析,`2024tb` 会被读成「2024 后面跟个 tb」,语法直接崩。 2. `@`、`@@`、`#`、`##` 各是什么? —— **`@` 局部变量,`@@` 系统函数,`#` 局部临时表,`##` 全局临时表。**临时表放在 tempdb 里, 不进你的数据文件,也不被备份——所以里面的数据丢了就丢了。 3. 列名叫 `order`,SQL 里怎么写? —— **写成 `[order]`。** 方括号是 SQL Server 的分隔标识符,意思是「这是名字不是关键字」。 MySQL 用反引号,Oracle 用双引号——跨库迁移时这三种写法要互相换。

下一篇 4.3 进正题:建库、改库、删库。 CREATE DATABASE 那段语法教材给得很完整,但真正要理解的是三组参数:初始大小、最大大小、 增长方式——它们决定这个库以后怎么长。

相关笔记