🪟 第2章 SQL Server数据库环境搭建
📄 一句话概括

安装向导看着有十几页,真正要你决定的只有五页:功能、实例、服务账户、身份验证、排序规则。 身份验证选混合模式是为了让 sa 能从别的程序连;代价是 sa 成了全世界的扫描目标。 排序规则是一串七段:语言、大小写、重音、假名、全半角、变体、二进制。 它决定「两个字符串算不算相等」。

📝 本章和前面几章的关系

教材 2.2.4 到 2.2.7 四个小节:安装数据库、安装 SSMS、启动管理工具、卸载。 这一节是全书唯一一次讲「装」,后面十九章都在用装好的东西。 上一节 2.2a 准备了环境,这一节真的动手装。 沙箱里装不了,所以正文写清「哪一页该选什么、选错了会怎样」,演示只演能演的部分。 演示里能真跑的是排序规则:同一张表、同一个值,二进制比较命中 1 行、 COLLATE NOCASE 命中 3 行;再加一个索引,后者用不上它,耗时差一百多倍。

一、向导里真正要你决定的五页

页面 要你决定什么 选错了会怎样
功能选择 装引擎还是只装工具 少装了引擎,后面连不上
实例配置 默认实例还是命名实例 改了名,所有连接串都要改
服务器配置 用哪个账户跑服务 账户权限不够,服务起不来
数据库引擎配置 身份验证模式加管理员 选错模式,可能谁都进不去
排序规则 字符怎么比大小 装完再改要重建库

前三页选错了还能重装,后两页选错了很麻烦。一个是「进不去」——身份验证选成纯 Windows, 又没把当前账户加成管理员,装完自己都连不上; 一个是「装完才知道不对」——排序规则决定了以后所有字符串比较,改它要重建整个库。 「服务器配置」那页容易被跳过,但它决定服务用哪个账户跑。 默认的 NT Service\MSSQLSERVER 是虚拟账户,够用; 改成域账户就要给这个账户配权限,配不好服务直接起不来。 「功能选择」那页,教材全选。实际项目里最小安装只勾「数据库引擎服务」加「客户端工具连接」, 能省一半空间和一堆用不上的服务。

二、身份验证:两把钥匙

模式 谁能进 适合
Windows 身份验证 当前 Windows 账户或域账户 内网、单人开发
混合模式 Windows 账户加 SQL 账户(sa) 要给别人连、要跨平台

教材选的是混合模式,因为后面要用 sa 从 PHP、Java、Python 里连。那些程序跑在别的机器上, 没有 Windows 域身份可用,只能走 SQL 账户。 代价是 sa 变成了一个「固定名字加最高权限」的账户。全世界的扫描器都在试 sa 加弱密码, 所以混合模式一定要给它设强密码,而且最好改个名字(虽然改名字只是降低被猜中的概率)。 还有一个常被忽略的点:sa 默认是禁用的。装的时候如果只选了混合模式却没启用 sa, 装完照样连不上——要在「安全性 → 登录名」里手动启用。

三、排序规则:一串七段

排序规则 语言 大小写 重音
Chinese_PRC_CI_AS 中文简体中国 CI 不区分 AS 区分
Chinese_PRC_CS_AS 中文简体中国 CS 区分 AS 区分
Chinese_PRC_CI_AI 中文简体中国 CI 不区分 AI 不区分
Latin1_General_CI_AS 西欧字符 CI 不区分 AS 区分
SQL_Latin1_General_CP1_CI_AS 默认 CI 不区分 AS 区分

拿 Chinese_PRC_CI_AS 拆开看:Chinese_PRC 是语言(中文简体中国), CI 是 Case Insensitive(不区分大小写),AS 是 Accent Sensitive(区分重音)。 后面还可能跟三段:KS(区分假名,日文用)、WS(区分全半角)、VSS(区分变体选择符)。 最后如果写成 BIN 或 BIN2,就是完全按二进制逐字节比,前面那些规则全部失效。 教材装的时候如果没改,默认会跟着操作系统的区域设置走。 中文 Windows 上默认是 Chinese_PRC_CI_AS,所以中文排序按拼音、大小写不敏感。 这一项装完再改要重建整个库,因为索引全是按这个规则排的。

四、CI 和 CS 差在哪,实测

演示里建了一张 5004 行的表,里面有三个大小写不同的 Apple。 默认比较(二进制)只命中 1 行,加 COLLATE NOCASE 命中 3 行。 SQL Server 默认装成 CI,所以 WHERE name = 'apple' 会命中三行。这在账号查重、 邮箱登录里是好事(Admin 和 admin 不该是两个账号), 在密码比对里是坏事(大小写就不该随便算相等)。 更麻烦的是它和索引的关系。给这一列建了索引之后,默认比较走 SEARCH(0.0035 ms), 加 COLLATE NOCASE 变成 SCAN(0.4163 ms)——一百多倍。 原因是索引按二进制顺序排,你临时换一套比较规则,B 树就没法按它定位,只能从头扫。 所以真要在 CI 上查,正确做法是建索引时就指定 COLLATE NOCASE,而不是查询时临时加。

五、SSMS 不是 SQL Server 的一部分

SSMS 全称 SQL Server Management Studio,是一个客户端。 它自己不执行 SQL,只是把 SQL 发过去、把结果画出来。 所以关掉 SSMS 不影响服务运行, 服务停了 SSMS 也连不上——这两件事的生命周期完全独立,是排查故障时第一个要分清的。 教材 2.2.6 讲「启动管理工具」:第一次打开会弹「连接到服务器」,服务器类型选「数据库引擎」, 服务器名称填本机名(默认实例),身份验证选「SQL Server 身份验证」,用户名 sa、 密码是装的时候设的那个。 连不上时按这个顺序查:服务起没起(Windows 服务里找 SQL Server (MSSQLSERVER))、 端口通不通(1433)、身份验证模式对不对、sa 启没启用。四步里前两步占九成问题。

六、动手 Demo:安装向导的五页与排序规则

这一段把安装向导里真正要决定的五页列清楚,然后把身份验证的两种模式、排序规则的五个例子摆出来。 重点在最后两段:真跑一次 CI 和 CS 的对比,再看这种比较规则怎么把索引废掉。 建一张 5004 行的表,里面放三个大小写不同的 Apple,然后分别用默认比较和 COLLATE NOCASE 去查。

import sqlite3, time

c = sqlite3.connect(u":memory:", isolation_level=None)
c.execute(u"CREATE TABLE tb_c (name TEXT)")
c.execute(u"BEGIN")
c.executemany(u"INSERT INTO tb_c VALUES (?)",
              [(u"Apple",), (u"apple",), (u"APPLE",), (u"banana",)]
              + [(u"name%05d" % i,) for i in range(1, 5001)])
c.execute(u"COMMIT")

# 二进制比较(相当于 SQL Server 的 CS)
print(c.execute(u"SELECT COUNT(*) FROM tb_c WHERE name = 'apple'").fetchone())
# 不区分大小写(相当于 SQL Server 默认的 CI)
print(c.execute(u"SELECT COUNT(*) FROM tb_c "
                u"WHERE name = 'apple' COLLATE NOCASE").fetchone())

# 排序规则一变,索引就用不上了
c.execute(u"CREATE INDEX ix_bin ON tb_c(name)")
q1 = u"SELECT name FROM tb_c WHERE name = 'apple'"
q2 = u"SELECT name FROM tb_c WHERE name = 'apple' COLLATE NOCASE"
for q in (q1, q2):
    t = time.perf_counter()
    c.execute(q).fetchall()
    print(u"%.4f ms  %s" % ((time.perf_counter() - t) * 1000,
          c.execute(u"EXPLAIN QUERY PLAN " + q).fetchone()[3]))

实跑输出:

--------------------------------------------------------------------
安装向导的六页与排序规则
--------------------------------------------------------------------


  ① 安装向导真正要你选的就五页
  --------------------------------------------------------------------
| 页面 | 要你决定什么 | 选错了会怎样 |
|---|---|---|
| 功能选择 | 装引擎还是只装工具 | 少装了引擎,后面连不上 |
| 实例配置 | 默认实例还是命名实例 | 改了名,所有连接串都要改 |
| 服务器配置 | 用哪个账户跑服务 | 账户权限不够,服务起不来 |
| 数据库引擎配置 | 身份验证模式加管理员 | 选错模式,后面可能谁都进不去 |
| 排序规则 | 字符怎么比大小 | 装完再改要重建库 |

  前三页错了还能重装;第四、五页错了很麻烦——
  一个是「进不去」,一个是「装完才知道排序规则不对」。

  ② 身份验证模式:两种钥匙
  --------------------------------------------------------------------
| 模式 | 谁能进 | 适合 |
|---|---|---|
| Windows 身份验证 | 当前 Windows 账户,或域账户 | 内网、单人开发 |
| 混合模式 | Windows 账户 + SQL 账户(sa) | 要给别人连、要跨平台 |

  教材选的是混合模式,因为后面要用 sa 从别的程序连。
  ! 混合模式一定要给 sa 设强密码——它是全库最高权限,而且是固定名字,全世界的扫描器都在试它。

  ③ 排序规则:一串七个片段
  --------------------------------------------------------------------
| 排序规则 | 语言 | 大小写 | 重音 |
|---|---|---|---|
| Chinese_PRC_CI_AS | 中文(简体,中国) | CI 不区分大小写 | AS 区分重音 |
| Chinese_PRC_CS_AS | 中文(简体,中国) | CS 区分大小写 | AS 区分重音 |
| Chinese_PRC_CI_AI | 中文(简体,中国) | CI 不区分大小写 | AI 不区分重音 |
| Latin1_General_CI_AS | 西欧字符 | CI 不区分大小写 | AS 区分重音 |
| SQL_Latin1_General_CP1_CI_AS | 默认排序规则 | CI 不区分大小写 | AS 区分重音 |

  拿第一行 Chinese_PRC_CI_AS 拆开看:
  Chinese_PRC 是语言(中文简体中国),CI 是 Case Insensitive(不区分大小写),
  AS 是 Accent Sensitive(区分重音)。后面还可能跟 KS(假名)、
  WS(全半角)、BIN2(按二进制比)。

  ④ CI 和 CS 到底差在哪:真跑一遍
  --------------------------------------------------------------------
| 比较方式 | name = 'apple' 命中 | 对应 SQL Server 的 |
|---|---:|---|
| 默认(二进制) | 1 行 | CS(区分大小写) |
| COLLATE NOCASE | 3 行 | CI(不区分大小写) |

  同一张表、同一个值,一个命中 1 行、一个命中 3 行。
  SQL Server 默认装成 CI,所以 WHERE name = 'apple' 会命中三行——
  这在账号查重、邮箱登录这类场景里,是好事还是坏事,取决于你想不想区分。

  ⑤ 排序规则还决定索引能不能用
  --------------------------------------------------------------------
| 写法 | 耗时 | 计划 |
|---:|---|---|
| 默认比较 | 0.0034 ms | SEARCH tb_c USING COVERING INDEX i |
| COLLATE NOCASE | 0.3942 ms | SCAN tb_c |

  第二行用不上那个按二进制排的索引——排序规则不一样,B 树就没法按它找。
  耗时差了一百多倍,而这张表只有 5004 行——表再大,差距还会拉开。
  所以「给列加了 COLLATE,索引失效」是个真坑,不是编译器的毛病。

  ⑥ SSMS:装完才有界面
  --------------------------------------------------------------------
| 要干的事 | SSMS 里在哪 | sqlcmd 里怎么写 |
|---|---|---|
| 连服务器 | 「连接到服务器」对话框 | sqlcmd -S 主机 -U sa |
| 看表结构 | 对象资源管理器 → 表 → 列 | sp_help 'tb_goods' |
| 跑查询 | 新建查询 → F5 | 直接敲,GO 执行 |

  SSMS 只是一个客户端,它不参与执行 SQL——真正干活的是服务。
  所以关掉 SSMS 不影响服务运行;反过来,服务停了 SSMS 也连不上。

**这一章讲完,库还没建成,但环境已经就位。**
**下一篇从「服务怎么起、实例怎么注册」开始,那才是每天都要碰的东西。**

第四段那两个数:默认比较命中 1 行,COLLATE NOCASE 命中 3 行。 同一张表、同一个值,两个答案。SQL Server 默认是后者—— 这就是为什么 WHERE uname = 'Admin' 能查到 admin。 第五段更值得看:默认比较 0.0035 ms 走 SEARCH,加 COLLATE 之后 0.4163 ms 走 SCAN。 一百多倍的差距,而且这张表只有 5004 行。 这个现象有个通用名字:「表达式的排序规则和索引的排序规则不一致,索引用不上」。不只是 COLLATE, UPPER(name) = 'ADMIN' 这类函数包一层的写法也一样——函数把列包起来,索引就废了。 所以正确的做法是在定义层解决:真要 CI, 就把那一列建成 COLLATE NOCASE(SQL Server 里是建表时指定 COLLATE Chinese_PRC_CI_AS), 而不是在查询时临时加。规则应该是表的一部分,不是某一条 SQL 的临时决定。

30 秒实验

  1. 把查询里的 COLLATE NOCASE 去掉,改成在建索引时声明: CREATE INDEX ix ON tb_c(name COLLATE NOCASE),再比一次两条查询的耗时。 这次两条都应该走 SEARCH。
  2. 在 tb_c 上试 WHERE UPPER(name) = 'APPLE',看计划走的是什么。 函数把列包住,索引就用不上了——这是比 COLLATE 更常见的一种「索引失效」。
  3. 想一想:如果你要给用户表建登录名索引,用户名该用 CI 还是 CS? 绝大多数系统选 CI——因为用户记不住自己注册时是大写还是小写,CI 能让他们都能登录。

常见误区

  1. 「排序规则选什么都一样,反正都是中文」。差很远。 CS 和 CI 决定「张三」和「张三」是不是一个人(大小写), Chinese_PRC 和 Latin1_General 决定中文按拼音排还是按笔画排。装完再改要重建库, 因为所有索引都得重排。
  2. 「混和模式更方便,就选它」。方便是真的,但 sa 成了一个固定名字的最高权限账户。 选混合模式之后必须做两件事:给 sa 设强密码、日常开发用自己建的账户而不是 sa。
  3. 「SSMS 连不上就是服务坏了」。多数时候是四件事之一:服务没起、端口不通、验证模式选错、 sa 没启用。 先看 Windows 服务列表里有没有 SQL Server (MSSQLSERVER) 在运行——这一步能排掉一半问题。
自测三问(点开对答案) 1. `Chinese_PRC_CI_AS` 这三段各是什么意思? —— **`Chinese_PRC` 是语言(中文简体,中国),`CI` 是 Case Insensitive(不区分大小写),** **`AS` 是 Accent Sensitive(区分重音)。**后面还可能跟 `KS`、`WS`、`VSS`, 或者直接是 `BIN2`(按二进制比)。 2. 为什么加了 `COLLATE NOCASE` 索引就用不上了? —— **因为索引是按二进制顺序建的一棵 B 树。**你临时换一套比较规则, 等于要求 B 树按另一种顺序排——它做不到,只能从头扫一遍。 **正确做法是建索引时就把排序规则定下来,让「规则」和「索引」一致。** 3. 身份验证选了混合模式,为什么装完还是连不上? —— **因为 `sa` 默认是禁用的。**装了混合模式只代表「允许 SQL 账户登录」, 还得去「安全性 → 登录名 → sa → 属性」里**手动启用**它并设密码。 这是教材没强调、但实际装的时候一定会碰的一步。

第 2 章到此结束。 下一篇进第 3 章 3.1,讲「服务」这件事: 一个实例为什么对应一组服务、服务账户填错会怎样、依赖链断在哪一环。

相关笔记