SQLServer-guide.md
... ...
@@ -0,0 +1,771 @@
1
+# Microsoft SQL Server 参考手册
2
+
3
+**SQL Server 版本**: 2019 (15.x)
4
+**镜像**: `mcr.microsoft.com/mssql/server:2019-latest`
5
+**指定中文字符集**: `COLLATE Chinese_PRC_CI_AS`
6
+
7
+> **重要提示**: 创建数据库时务必指定字符集,否则中文会出现乱码:
8
+>
9
+> ```sql
10
+> CREATE DATABASE snest_dev COLLATE Chinese_PRC_CI_AS;
11
+> ```
12
+
13
+---
14
+
15
+## 目录
16
+
17
+1. [安装与部署](#第一节-安装与部署)
18
+2. [连接与配置](#第二节-连接与配置)
19
+3. [核心特性与三数据库对比](#第三节-核心特性与三数据库对比)
20
+4. [Java 适配开发](#第四节-java-适配开发)
21
+5. [参考文档与附录](#第五节-参考文档与附录)
22
+
23
+---
24
+
25
+## 第一节. 安装与部署
26
+
27
+### 1.1 前置准备
28
+
29
+| 资源 | 要求 |
30
+| :----------- | :----------------------------------------------------------- |
31
+| **操作系统** | Windows 10/11、Windows Server 2016+、Linux (Ubuntu 16.04+、RHEL 7+)、Docker 19.03+ |
32
+| **安装包** | `mcr.microsoft.com/mssql/server:2019-latest` |
33
+| **端口** | 宿主机 1433(映射容器 1433) |
34
+| **内存** | 最低 2GB RAM(推荐 4GB+) |
35
+| **磁盘** | 至少 6GB 可用空间 |
36
+| **用户权限** | 具有 sudo 权限的普通用户 |
37
+
38
+### 1.2 Docker 安装
39
+
40
+#### 拉取镜像
41
+
42
+```bash
43
+# 拉取 SQL Server 2019 最新版
44
+docker pull mcr.microsoft.com/mssql/server:2019-latest
45
+
46
+# 或拉取特定 CU 版本
47
+docker pull mcr.microsoft.com/mssql/server:2019-CU27-ubuntu-20.04
48
+```
49
+
50
+#### 目录与权限
51
+
52
+```bash
53
+# 创建数据目录
54
+sudo mkdir -p /usr/sie/mssql/{data,log,backup}
55
+
56
+# 设置权限(容器内 mssql 用户 UID 10001)
57
+sudo chown -R 10001:10001 /usr/sie/mssql
58
+```
59
+
60
+#### 创建并启动容器
61
+
62
+```bash
63
+docker run -d --privileged=true \
64
+ -e "ACCEPT_EULA=Y" \
65
+ -e "MSSQL_SA_PASSWORD=SNEST_TEST#123" \
66
+ -e "MSSQL_PID=Developer" \
67
+ -v /usr/sie/mssql/data:/var/opt/mssql/data \
68
+ -v /usr/sie/mssql/log:/var/opt/mssql/log \
69
+ -v /usr/sie/mssql/backup:/var/opt/mssql/backup \
70
+ --name mssql2019 \
71
+ --hostname mssql2019 \
72
+ -p 1433:1433 \
73
+ mcr.microsoft.com/mssql/server:2019-latest
74
+```
75
+
76
+**环境变量说明**:
77
+
78
+| 变量 | 说明 |
79
+| :------------------ | :-------------------------------------------------- |
80
+| `ACCEPT_EULA=Y` | 接受许可协议(**必须**) |
81
+| `MSSQL_SA_PASSWORD` | SA 密码(≥8位,含大小写+数字+特殊字符) |
82
+| `MSSQL_PID` | 版本:`Developer`/`Express`/`Standard`/`Enterprise` |
83
+| `MSSQL_COLLATION` | 排序规则,如 `Chinese_PRC_CI_AS` |
84
+| `MSSQL_LCID` | 语言 ID,`2052` 为简体中文 |
85
+| `MSSQL_TCP_PORT` | TCP 端口,默认 1433 |
86
+
87
+**一键启动脚本**:
88
+
89
+```bash
90
+docker run -d --privileged=true -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=SNEST_TEST#123" -e "MSSQL_PID=Developer" -v /usr/sie/mssql/data:/var/opt/mssql/data -v /usr/sie/mssql/log:/var/opt/mssql/log -v /usr/sie/mssql/backup:/var/opt/mssql/backup --name mssql2019 -p 1433:1433 mcr.microsoft.com/mssql/server:2019-latest
91
+```
92
+
93
+#### 验证启动
94
+
95
+```bash
96
+# 查看日志
97
+docker logs -f --tail 100 mssql2019
98
+
99
+# 查看容器状态
100
+docker ps | grep mssql2019
101
+```
102
+
103
+成功标志:`SQL Server is now ready for client connections.`
104
+
105
+#### 进入容器
106
+
107
+```bash
108
+docker exec -it mssql2019 bash
109
+
110
+# 容器内连接
111
+/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P "SNEST_TEST#123"
112
+```
113
+
114
+### 1.3 数据库与用户初始化
115
+
116
+#### 创建业务数据库及用户
117
+
118
+```sql
119
+-- 使用 SA 登录
120
+USE master;
121
+GO
122
+
123
+-- 创建数据库(务必指定字符集)
124
+CREATE DATABASE snest_dev COLLATE Chinese_PRC_CI_AS;
125
+GO
126
+
127
+-- 创建登录名(服务器级别)
128
+CREATE LOGIN snest_dev
129
+WITH PASSWORD = 'SNEST_DEV#123',
130
+CHECK_POLICY = OFF;
131
+GO
132
+
133
+USE snest_dev;
134
+GO
135
+
136
+-- 创建用户(数据库级别)
137
+CREATE USER snest_dev FOR LOGIN snest_dev;
138
+GO
139
+
140
+-- 创建 Schema(与用户不同名,推荐)
141
+CREATE SCHEMA dev_sch AUTHORIZATION snest_dev;
142
+GO
143
+
144
+-- 设置默认 Schema
145
+ALTER USER snest_dev WITH DEFAULT_SCHEMA = dev_sch;
146
+GO
147
+
148
+-- 授权
149
+GRANT CONTROL ON SCHEMA::dev_sch TO snest_dev;
150
+GO
151
+```
152
+
153
+#### 验证配置
154
+
155
+```sql
156
+-- 查看用户及默认 Schema
157
+SELECT
158
+ name AS UserName,
159
+ default_schema_name AS DefaultSchema
160
+FROM sys.database_principals
161
+WHERE name = 'snest_dev';
162
+
163
+-- 查看 Schema
164
+SELECT
165
+ name AS SchemaName
166
+FROM sys.schemas
167
+WHERE name = 'dev_sch';
168
+```
169
+
170
+### 1.4 常用运维命令
171
+
172
+| 操作 | 命令 |
173
+| :--------- | :----------------------------------------------------------- |
174
+| 停止数据库 | `docker stop mssql2019` |
175
+| 启动数据库 | `docker start mssql2019` |
176
+| 查看日志 | `docker logs mssql2019` |
177
+| 实时日志 | `docker exec -it mssql2019 tail -f /var/opt/mssql/log/errorlog` |
178
+| 全库备份 | `docker exec -it mssql2019 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "xxx" -Q "BACKUP DATABASE [snest_dev] TO DISK = '/var/opt/mssql/backup/snest_dev_$(date +%F).bak'"` |
179
+| 全库恢复 | `docker exec -it mssql2019 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "xxx" -Q "RESTORE DATABASE [snest_dev] FROM DISK = '/var/opt/mssql/backup/snest_dev_xxxx.bak'"` |
180
+| 完全卸载 | `docker stop mssql2019 && docker rm mssql2019 && sudo rm -rf /usr/sie/mssql` |
181
+
182
+---
183
+
184
+## 第二节. 连接与配置
185
+
186
+### 2.1 客户端连接
187
+
188
+#### DBeaver 配置
189
+
190
+| 配置项 | 值 |
191
+| :----- | :----------------------------------- |
192
+| 主机 | `<宿主机IP>` |
193
+| 端口 | `1433` |
194
+| 数据库 | `snest_dev` |
195
+| 用户名 | `snest_dev` |
196
+| 驱动 | Microsoft JDBC Driver for SQL Server |
197
+
198
+> 注意:DBeaver 默认使用 `mssql-jdbc` 驱动
199
+
200
+### 2.2 JDBC 配置
201
+
202
+#### 标准配置
203
+
204
+```properties
205
+######## SQL Server DBCP ##########
206
+driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
207
+url=jdbc:sqlserver://ip:1433;databaseName=snest_dev;encrypt=true;trustServerCertificate=true
208
+username=snest_dev
209
+password=******
210
+```
211
+
212
+#### 集成环境配置
213
+
214
+```properties
215
+######## 集成环境 DBCP ##########
216
+driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
217
+url=jdbc:sqlserver://ip:1433;databaseName=snest_dev;encrypt=true;trustServerCertificate=true;sendStringParametersAsUnicode=false
218
+username=snest_dev
219
+password=******
220
+```
221
+
222
+#### 常用 URL 参数
223
+
224
+| 参数 | 说明 |
225
+| :------------------------------------ | :------------------------------ |
226
+| `encrypt=true` | 启用 SSL 加密(2019+ 建议启用) |
227
+| `trustServerCertificate=true` | 信任服务器证书(开发环境) |
228
+| `sendStringParametersAsUnicode=false` | 禁用 Unicode 发送,提升性能 |
229
+| `loginTimeout=30` | 登录超时(秒) |
230
+| `socketTimeout=90` | 套接字超时(秒) |
231
+| `applicationName=snest-app` | 应用程序标识 |
232
+
233
+---
234
+
235
+## 第三节. 核心特性与三数据库对比
236
+
237
+### 3.1 标识符与引号
238
+
239
+#### SQL Server 标识符规则
240
+
241
+| 场景 | 行为 |
242
+| :------------- | :----------------------------------------- |
243
+| 大小写敏感 | 默认**不敏感**(取决于排序规则 `CI`/`CS`) |
244
+| 方括号 `[]` | SQL Server 特有,推荐用于关键字/特殊字符 |
245
+| 双引号 `""` | 仅当 `QUOTED_IDENTIFIER ON` 时作为标识符 |
246
+| 单引号 `''` | 字符串字面量 |
247
+| Unicode 字符串 | `N'中文'` 前缀 |
248
+
249
+```sql
250
+-- 大小写不敏感
251
+SELECT * FROM MyTable; -- 等同于 MYTABLE、mytable
252
+
253
+-- 方括号包裹关键字
254
+SELECT [SELECT], [FROM] FROM [My Table]; -- 表名含空格
255
+
256
+-- QUOTED_IDENTIFIER ON 时使用双引号
257
+SET QUOTED_IDENTIFIER ON;
258
+SELECT * FROM "MyTable";
259
+```
260
+
261
+#### QUOTED_IDENTIFIER 详解
262
+
263
+| 设置 | 双引号行为 | 使用场景 |
264
+| :----------- | :------------------ | :------------------------------- |
265
+| `ON`(默认) | 标识符(表名/列名) | 索引视图、计算列索引、保留关键字 |
266
+| `OFF` | 字符串字面量 | 不推荐,仅兼容性场景 |
267
+
268
+**重要限制**:
269
+
270
+- 创建索引视图、计算列索引、XML索引时**必须**为 `ON`
271
+- 存储过程创建时捕获当前设置,后续调用沿用该设置
272
+
273
+```sql
274
+-- 标准存储过程模板
275
+SET ANSI_NULLS ON;
276
+GO
277
+SET QUOTED_IDENTIFIER ON;
278
+GO
279
+CREATE PROCEDURE usp_GetCustomers
280
+AS
281
+BEGIN
282
+ SELECT [name], [address] FROM [customers];
283
+END
284
+GO
285
+```
286
+
287
+#### 三数据库引号对比
288
+
289
+| 特性 | SQL Server | PostgreSQL | MySQL |
290
+| :------------ | :-------------------------------------- | :--------- | :------------- |
291
+| 标识符大小写 | 不敏感(CI排序规则) | 转小写存储 | 取决于OS |
292
+| 标识符引号 | `[]` / `""`(需`QUOTED_IDENTIFIER ON`) | `""` | `` ` `` / `""` |
293
+| 字符串引号 | `''` | `''` | `''` / `""` |
294
+| Unicode字符串 | `N'...'` | 直接支持 | 直接支持 |
295
+
296
+---
297
+
298
+### 3.2 数据类型
299
+
300
+#### 日期时间类型(重点)
301
+
302
+| Java 类型 | SQL Server | MySQL | PostgreSQL |
303
+| :------------------- | :------------------------------ | :----------------------- | :-------------------------- |
304
+| `java.sql.Date` | `DATE` | `DATE` | `DATE` |
305
+| `java.sql.Time` | `TIME` | `TIME` | `TIME` |
306
+| `java.sql.Timestamp` | `DATETIME2`(推荐)/ `DATETIME` | `DATETIME` / `TIMESTAMP` | `TIMESTAMP` / `TIMESTAMPTZ` |
307
+| 时间戳自增 | `ROWVERSION`(原`TIMESTAMP`) | `TIMESTAMP`(2038问题) | 无(用触发器) |
308
+
309
+**⚠️ 关键陷阱**:SQL Server 的 `TIMESTAMP` **不是时间类型**,而是二进制自增戳(`ROWVERSION`),与 MySQL/PostgreSQL 的 `TIMESTAMP` 完全不同!
310
+
311
+```sql
312
+-- SQL Server 日期时间
313
+SELECT GETDATE(); -- DATETIME,精度3.33ms
314
+SELECT SYSDATETIME(); -- DATETIME2,精度100ns(推荐)
315
+SELECT GETUTCDATE(); -- UTC时间
316
+
317
+-- MySQL
318
+SELECT NOW(); -- 当前时间
319
+SELECT UTC_TIMESTAMP(); -- UTC时间
320
+
321
+-- PostgreSQL
322
+SELECT CURRENT_TIMESTAMP; -- 带时区
323
+SELECT NOW(); -- 同 CURRENT_TIMESTAMP
324
+```
325
+
326
+**JDBC 跨数据库兼容写法**:
327
+
328
+```java
329
+// 所有数据库通用
330
+ps.setObject(parameterIndex, timestamp, java.sql.Types.TIMESTAMP);
331
+```
332
+
333
+#### 字符串类型
334
+
335
+| 类型 | SQL Server | MySQL | PostgreSQL |
336
+| :---------- | :------------------------------- | :------------------------ | :--------------------------- |
337
+| 定长字符 | `CHAR(n)` | `CHAR(n)` | `CHAR(n)` |
338
+| 变长字符 | `VARCHAR(n)`(最大8000) | `VARCHAR(n)`(最大65535) | `VARCHAR(n)`(实际同`TEXT`) |
339
+| Unicode变长 | `NVARCHAR(n)`(推荐中文) | 无需(UTF-8直接存储) | 无需(UTF-8直接存储) |
340
+| 大文本 | `VARCHAR(MAX)` / `NVARCHAR(MAX)` | `TEXT` / `LONGTEXT` | `TEXT`(无限制) |
341
+| 已废弃 | `TEXT` / `NTEXT` | - | - |
342
+
343
+```sql
344
+-- SQL Server Unicode(必须加N前缀)
345
+INSERT INTO users (name) VALUES (N'张三');
346
+
347
+-- MySQL/PostgreSQL(直接插入)
348
+INSERT INTO users (name) VALUES ('张三');
349
+```
350
+
351
+#### 布尔类型
352
+
353
+| 数据库 | 类型 | 值 |
354
+| :------------- | :---------------------------- | :------------------------ |
355
+| **SQL Server** | `BIT` | `0` / `1` / `NULL` |
356
+| **MySQL** | `BOOLEAN`(实际`TINYINT(1)`) | `0` / `1` |
357
+| **PostgreSQL** | `BOOLEAN` | `true` / `false` / `NULL` |
358
+
359
+
360
+
361
+---
362
+
363
+### 3.3 分页语法(重点)
364
+
365
+| 数据库 | 语法 | ORDER BY 要求 |
366
+| :------------- | :------------------------------------- | :------------ |
367
+| **SQL Server** | `OFFSET x ROWS FETCH NEXT y ROWS ONLY` | **必须** |
368
+| **MySQL** | `LIMIT offset, count` | 可选 |
369
+| **PostgreSQL** | `LIMIT count OFFSET offset` | 可选 |
370
+
371
+```sql
372
+-- SQL Server(2012+,必须带ORDER BY)
373
+SELECT * FROM users
374
+WHERE status = 1
375
+ORDER BY id
376
+OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
377
+
378
+-- MySQL
379
+SELECT * FROM users WHERE status = 1 ORDER BY id LIMIT 0, 10;
380
+
381
+-- PostgreSQL
382
+SELECT * FROM users WHERE status = 1 ORDER BY id LIMIT 10 OFFSET 0;
383
+```
384
+
385
+**Provider 实现**:
386
+
387
+```java
388
+// SQL Server
389
+public String getPageSQL(String sql, int pageNo, int pageSize) {
390
+ int offset = (pageNo - 1) * pageSize;
391
+ return sql + " OFFSET " + offset + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY";
392
+}
393
+
394
+// MySQL
395
+public String getPageSQL(String sql, int pageNo, int pageSize) {
396
+ int offset = (pageNo - 1) * pageSize;
397
+ return sql + " LIMIT " + offset + ", " + pageSize;
398
+}
399
+
400
+// PostgreSQL
401
+public String getPageSQL(String sql, int pageNo, int pageSize) {
402
+ int offset = (pageNo - 1) * pageSize;
403
+ return sql + " LIMIT " + pageSize + " OFFSET " + offset;
404
+}
405
+```
406
+
407
+---
408
+
409
+### 3.4 UPDATE 语句别名(重点)
410
+
411
+| 数据库 | 单表UPDATE语法 | 特点 |
412
+| :------------- | :---------------------------------------- | :--------------- |
413
+| **SQL Server** | `UPDATE t SET ... FROM table t WHERE ...` | **必须用`FROM`** |
414
+| **MySQL** | `UPDATE table t SET ... WHERE ...` | 最灵活,直接别名 |
415
+| **PostgreSQL** | `UPDATE table t SET ... WHERE ...` | 单表直接别名 |
416
+
417
+```sql
418
+-- ❌ SQL Server 不支持(MySQL/PostgreSQL风格)
419
+UPDATE [t] SET [update_user]=?, [update_date]=?
420
+FROM [tenant_user_instance] AS [t]
421
+WHERE [t].[id] IN (?);
422
+
423
+-- ✅ SQL Server 正确写法
424
+UPDATE t
425
+SET update_user = ?, update_date = SYSDATETIME()
426
+FROM tenant_user_instance t
427
+WHERE t.id = ?;
428
+
429
+-- MySQL/PostgreSQL 风格
430
+UPDATE tenant_user_instance t
431
+SET t.update_user = ?, t.update_date = NOW()
432
+WHERE t.id = ?;
433
+```
434
+
435
+---
436
+
437
+### 3.5 自增列
438
+
439
+| 特性 | SQL Server | MySQL | PostgreSQL |
440
+| :--------- | :----------------------------------------------------------- | :---------------------- | :---------------------------------------- |
441
+| 语法 | `IDENTITY(1,1)` | `AUTO_INCREMENT` | `SERIAL` / `GENERATED ALWAYS AS IDENTITY` |
442
+| 获取当前值 | `SCOPE_IDENTITY()`(推荐)<br>`@@IDENTITY`<br>`IDENT_CURRENT('table')` | `LAST_INSERT_ID()` | `CURRVAL('seq')` / `RETURNING id` |
443
+| 显式插入 | `SET IDENTITY_INSERT ON/OFF` | 直接插入(0或NULL触发) | `OVERRIDING SYSTEM VALUE` |
444
+| 序列对象 | 独立`SEQUENCE` | 无 | `SEQUENCE`(SERIAL底层) |
445
+
446
+```sql
447
+-- SQL Server 自增
448
+CREATE TABLE users (id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(50));
449
+INSERT INTO users (name) VALUES (N'张三');
450
+SELECT SCOPE_IDENTITY(); -- 获取刚插入的ID
451
+
452
+-- 显式插入(数据迁移)
453
+SET IDENTITY_INSERT users ON;
454
+INSERT INTO users (id, name) VALUES (100, N'迁移数据');
455
+SET IDENTITY_INSERT users OFF;
456
+```
457
+
458
+**限制**:`SET IDENTITY_INSERT` 一次只能对一个表设置为 `ON`。
459
+
460
+---
461
+
462
+### 3.6 Schema 管理
463
+
464
+| 特性 | SQL Server | MySQL | PostgreSQL |
465
+| :-------------- | :------------------- | :---------------- | :------------------- |
466
+| Schema概念 | 独立对象 | Schema = Database | 独立对象 |
467
+| 默认Schema | `dbo` | 数据库本身 | `public` |
468
+| 用户-Schema关系 | 分离(多对多) | 无独立Schema | 分离(多对多) |
469
+| 搜索路径 | 无,使用默认Schema | 无 | `search_path`控制 |
470
+| 跨Schema访问 | 同一数据库内直接访问 | 跨库需FEDERATED | 同一数据库内直接访问 |
471
+
472
+```sql
473
+-- SQL Server Schema 管理
474
+CREATE SCHEMA dev_sch AUTHORIZATION snest_dev;
475
+ALTER USER snest_dev WITH DEFAULT_SCHEMA = dev_sch;
476
+GRANT CONTROL ON SCHEMA::dev_sch TO snest_dev;
477
+
478
+-- 查询时
479
+SELECT * FROM dev_sch.mytable; -- 完整限定名
480
+SELECT * FROM mytable; -- 使用默认Schema(如果设置了)
481
+```
482
+
483
+**Schema 不可见问题排查**:
484
+
485
+```sql
486
+-- 查看当前用户默认Schema
487
+SELECT default_schema_name FROM sys.database_principals WHERE name = USER;
488
+
489
+-- 查看所有Schema
490
+SELECT name FROM sys.schemas;
491
+
492
+-- 查看Schema下的表
493
+SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('dev_sch');
494
+```
495
+
496
+---
497
+
498
+### 3.7 事务与 DDL
499
+
500
+| 数据库 | Transactional DDL | 说明 |
501
+| :------------- | :---------------- | :------------------------------------- |
502
+| **SQL Server** | **部分支持** | 大部分DDL可回滚,数据库相关DDL隐式提交 |
503
+| **PostgreSQL** | **完全支持** | 几乎所有DDL都在事务中 |
504
+| **MySQL** | **不支持** | DDL自动隐式提交 |
505
+
506
+```sql
507
+-- SQL Server(部分支持)
508
+BEGIN TRANSACTION;
509
+CREATE TABLE test (id INT); -- 可回滚
510
+-- CREATE DATABASE test2; -- 隐式提交,无法回滚
511
+ROLLBACK;
512
+
513
+-- PostgreSQL(完全支持)
514
+BEGIN;
515
+CREATE TABLE test (id INT); -- 在事务中
516
+ROLLBACK; -- 完全回滚
517
+
518
+-- MySQL(不支持)
519
+START TRANSACTION;
520
+CREATE TABLE test (id INT); -- 隐式提交!
521
+ROLLBACK; -- 无效,表已创建
522
+```
523
+
524
+**SQL Server 隐式提交的 DDL**:
525
+
526
+- `CREATE DATABASE` / `ALTER DATABASE` / `DROP DATABASE`
527
+- `CREATE/DROP FULLTEXT INDEX`
528
+
529
+---
530
+
531
+### 3.8 索引
532
+
533
+| 特性 | SQL Server | MySQL | PostgreSQL |
534
+| :----------- | :----------------------- | :----------------- | :------------------- |
535
+| 聚集索引 | 支持(一个表只能有一个) | 仅InnoDB主键聚集 | 不支持 |
536
+| 索引长度限制 | 1700字节 | 3072字节(InnoDB) | 理论上无限制 |
537
+| 全文索引 | 内置 | `FULLTEXT` | `tsvector`/`tsquery` |
538
+| 部分索引 | 不支持 | 不支持 | 支持(`WHERE`条件) |
539
+| 表达式索引 | 不支持(可用计算列) | 不支持 | 支持 |
540
+
541
+```sql
542
+-- SQL Server 索引
543
+CREATE INDEX IX_users_email ON users(email);
544
+CREATE UNIQUE INDEX UQ_users_phone ON users(phone);
545
+CREATE CLUSTERED INDEX IX_users_created ON users(created_date); -- 聚集索引
546
+CREATE INDEX IX_users_name ON users(name) INCLUDE (email, phone); -- 包含列
547
+```
548
+
549
+---
550
+
551
+### 3.9 JSON 支持(2016+)
552
+
553
+| 特性 | SQL Server | MySQL | PostgreSQL |
554
+| :------- | :-------------------------------- | :------------------------------- | :------------------------- |
555
+| 存储类型 | `NVARCHAR(MAX)` | `JSON`(5.7+) | `JSON` / `JSONB`(二进制) |
556
+| 提取字段 | `JSON_VALUE()` / `JSON_QUERY()` | `JSON_EXTRACT()` / `->` / `->>` | `->` / `->>` / `#>` |
557
+| 更新JSON | `JSON_MODIFY()` | `JSON_SET()` | `jsonb_set()` |
558
+| 表转JSON | `FOR JSON AUTO` / `FOR JSON PATH` | `JSON_OBJECT()` / `JSON_ARRAY()` | `json_agg()` / `to_json()` |
559
+| 索引 | 计算列索引 | 虚拟列索引(8.0+) | GIN索引(JSONB) |
560
+
561
+```sql
562
+-- SQL Server JSON
563
+DECLARE @json NVARCHAR(MAX) = N'{"name": "张三", "age": 30}';
564
+SELECT JSON_VALUE(@json, '$.name'); -- 标量值
565
+SELECT JSON_QUERY(@json, '$.skills'); -- 对象/数组
566
+SELECT * FROM users FOR JSON AUTO; -- 表转JSON
567
+
568
+-- JSON索引
569
+ALTER TABLE users ADD json_name AS JSON_VALUE(info, '$.name');
570
+CREATE INDEX IX_users_json_name ON users(json_name);
571
+```
572
+
573
+---
574
+
575
+### 3.10 类型转换
576
+
577
+| 操作 | SQL Server | MySQL | PostgreSQL |
578
+| :--------- | :------------------------------ | :----------------- | :---------------- |
579
+| 标准转换 | `CAST(x AS type)` | `CAST(x AS type)` | `CAST(x AS type)` |
580
+| 特有语法 | `CONVERT(type, x)` | `CONVERT(x, type)` | `x::type` |
581
+| 安全转换 | `TRY_CAST` / `TRY_CONVERT` | 无 | 无 |
582
+| 日期格式化 | `CONVERT(VARCHAR, date, style)` | `DATE_FORMAT()` | `TO_CHAR()` |
583
+
584
+```sql
585
+-- SQL Server 样式代码
586
+SELECT CONVERT(VARCHAR, GETDATE(), 120); -- yyyy-mm-dd hh:mi:ss(24h)
587
+SELECT CONVERT(VARCHAR, GETDATE(), 121); -- yyyy-mm-dd hh:mi:ss.mmm(含毫秒)
588
+SELECT CONVERT(VARCHAR, GETDATE(), 112); -- yyyymmdd
589
+```
590
+
591
+---
592
+
593
+### 3.11 字符串连接
594
+
595
+| 数据库 | 连接符 | NULL处理 |
596
+| :------------- | :----------------------------------------- | :------------------------------------- |
597
+| **SQL Server** | `+` / `CONCAT()` | `+`传播NULL,`CONCAT()`视NULL为空串 |
598
+| **MySQL** | `CONCAT()` / `\|\|`(需`PIPES_AS_CONCAT`) | `CONCAT()`视NULL为空串 |
599
+| **PostgreSQL** | `\|\|` / `CONCAT()` | `\|\|`传播NULL,`CONCAT()`视NULL为空串 |
600
+
601
+```sql
602
+-- SQL Server
603
+SELECT 'Hello' + ' ' + 'World'; -- Hello World
604
+SELECT 'Hello' + NULL; -- NULL(传播)
605
+SELECT CONCAT('Hello', NULL, 'World'); -- HelloWorld(NULL视为空串)
606
+
607
+-- 分组聚合(2017+)
608
+SELECT STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY name) FROM users;
609
+```
610
+
611
+---
612
+
613
+### 3.12 日期函数
614
+
615
+| 操作 | SQL Server | MySQL | PostgreSQL |
616
+| :--------- | :------------------------------------ | :--------------------------------- | :---------------------------- |
617
+| 当前时间戳 | `GETDATE()` / `SYSDATETIME()` | `NOW()` / `SYSDATE()` | `CURRENT_TIMESTAMP` / `NOW()` |
618
+| 当前日期 | `CAST(GETDATE() AS DATE)` | `CURDATE()` | `CURRENT_DATE` |
619
+| 加1天 | `DATEADD(DAY, 1, date)` | `DATE_ADD(date, INTERVAL 1 DAY)` | `date + INTERVAL '1 day'` |
620
+| 加1月 | `DATEADD(MONTH, 1, date)` | `DATE_ADD(date, INTERVAL 1 MONTH)` | `date + INTERVAL '1 month'` |
621
+| 日期差 | `DATEDIFF(DAY, d1, d2)` | `DATEDIFF(d1, d2)` | `d2 - d1` |
622
+| 提取年份 | `YEAR(date)` / `DATEPART(YEAR, date)` | `YEAR(date)` | `EXTRACT(YEAR FROM date)` |
623
+
624
+---
625
+
626
+### 3.13 系统视图
627
+
628
+| 信息类型 | SQL Server | MySQL | PostgreSQL |
629
+| :------- | :----------------------------------------- | :------------------------------------- | :---------------------------------------- |
630
+| 所有表 | `sys.tables` / `INFORMATION_SCHEMA.TABLES` | `INFORMATION_SCHEMA.TABLES` | `pg_tables` / `INFORMATION_SCHEMA.TABLES` |
631
+| 所有列 | `sys.columns` | `INFORMATION_SCHEMA.COLUMNS` | `information_schema.columns` |
632
+| 索引信息 | `sys.indexes` | `INFORMATION_SCHEMA.STATISTICS` | `pg_indexes` |
633
+| 约束信息 | `sys.key_constraints` | `INFORMATION_SCHEMA.TABLE_CONSTRAINTS` | `information_schema.table_constraints` |
634
+| 存储过程 | `sys.procedures` | `INFORMATION_SCHEMA.ROUTINES` | `pg_proc` |
635
+| 当前用户 | `SUSER_SNAME()` / `USER_NAME()` | `CURRENT_USER()` | `CURRENT_USER` |
636
+
637
+**对比**:
638
+
639
+- `sys` 视图:SQL Server特有,性能更好,信息更详细
640
+- `INFORMATION_SCHEMA`:ANSI标准,跨数据库兼容
641
+
642
+```sql
643
+-- SQL Server 推荐(sys视图)
644
+SELECT * FROM sys.tables WHERE name = 'users';
645
+SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('users');
646
+
647
+-- 跨数据库兼容(INFORMATION_SCHEMA)
648
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'users';
649
+```
650
+
651
+---
652
+
653
+### 3.14 错误处理
654
+
655
+```sql
656
+-- SQL Server TRY...CATCH
657
+BEGIN TRY
658
+ BEGIN TRANSACTION;
659
+ INSERT INTO users (id) VALUES (1);
660
+ INSERT INTO users (id) VALUES (1); -- 主键冲突
661
+ COMMIT;
662
+END TRY
663
+BEGIN CATCH
664
+ IF @@TRANCOUNT > 0 ROLLBACK;
665
+ SELECT
666
+ ERROR_NUMBER() AS ErrorNumber,
667
+ ERROR_MESSAGE() AS ErrorMessage,
668
+ ERROR_LINE() AS ErrorLine;
669
+END CATCH;
670
+```
671
+
672
+**对比**:
673
+
674
+- SQL Server:`TRY...CATCH`,可在CATCH块中决定回滚或继续
675
+- PostgreSQL:事务进入中止状态,必须回滚
676
+- MySQL:存储过程中使用`DECLARE CONTINUE/EXIT HANDLER`
677
+
678
+---
679
+
680
+## 第四节. Java 适配开发
681
+
682
+### 4.1 SQLServerProvider 实现
683
+
684
+```java
685
+public class SQLServerProvider extends AbstractSQLProvider {
686
+
687
+ @Override
688
+ public String getDBType() {
689
+ return DBType.SQLServer.getName();
690
+ }
691
+
692
+ @Override
693
+ public String getPageSQL(String sql, int pageNo, int pageSize) {
694
+ int offset = (pageNo - 1) * pageSize;
695
+ return sql + " OFFSET " + offset + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY";
696
+ }
697
+
698
+ @Override
699
+ public String getCurrentTimeSQL() {
700
+ return "SELECT SYSDATETIME()";
701
+ }
702
+}
703
+```
704
+
705
+### 4.2 关键适配点
706
+
707
+| 适配项 | SQL Server 特性 | 注意事项 |
708
+| :--------- | :----------------------------------- | :----------------------------------- |
709
+| 分页 | `OFFSET ... FETCH` 必须带 `ORDER BY` | 确保原始SQL已包含排序 |
710
+| 自增ID | `SCOPE_IDENTITY()` | 当前会话当前作用域 |
711
+| 布尔值 | `BIT` 类型,`1`/`0` | 非 `true`/`false` |
712
+| Unicode | 字符串加 `N` 前缀 | `N'中文'` |
713
+| 日期时间 | 推荐 `DATETIME2` | 避免使用 `TIMESTAMP`(是二进制类型) |
714
+| UPDATE别名 | 必须用 `FROM` 语法 | 不支持单表直接别名 |
715
+| Schema | 默认 `dbo`,用户与Schema分离 | 注意权限配置 |
716
+
717
+---
718
+
719
+## 第五节. 参考文档与附录
720
+
721
+### 5.1 官方文档
722
+
723
+| 主题 | 链接 |
724
+| :----------------------- | :----------------------------------------------------------- |
725
+| SQL Server 2019 官方文档 | https://docs.microsoft.com/zh-cn/sql/sql-server/ |
726
+| SQL Server on Linux | https://docs.microsoft.com/zh-cn/sql/linux/ |
727
+| Docker 快速入门 | https://docs.microsoft.com/zh-cn/sql/linux/quickstart-install-connect-docker |
728
+| JDBC 驱动文档 | https://docs.microsoft.com/zh-cn/sql/connect/jdbc/ |
729
+| T-SQL 参考 | https://docs.microsoft.com/zh-cn/sql/t-sql/ |
730
+
731
+### 5.2 兼容性速查表
732
+
733
+| 操作 | SQL Server | PostgreSQL | MySQL |
734
+| :--------- | :------------------------------- | :------------------------ | :------------------------------ |
735
+| 当前时间 | `GETDATE()` / `SYSDATETIME()` | `NOW()` | `NOW()` / `SYSDATE()` |
736
+| 分页 | `OFFSET ... FETCH`(需ORDER BY) | `LIMIT ... OFFSET` | `LIMIT ... OFFSET` |
737
+| 自增ID | `IDENTITY(1,1)` | `SERIAL` / `IDENTITY` | `AUTO_INCREMENT` |
738
+| 获取自增ID | `SCOPE_IDENTITY()` | `CURRVAL()` / `RETURNING` | `LAST_INSERT_ID()` |
739
+| 字符串连接 | `+` / `CONCAT()` | `\|\|` / `CONCAT()` | `CONCAT()` |
740
+| 空值替换 | `ISNULL()` / `COALESCE()` | `COALESCE()` | `IFNULL()` / `COALESCE()` |
741
+| 类型转换 | `CAST()` / `CONVERT()` | `CAST()` / `::` | `CAST()` / `CONVERT()` |
742
+| 布尔类型 | `BIT` (0/1) | `BOOLEAN` (true/false) | `BOOLEAN` / `TINYINT` (0/1) |
743
+| JSON提取 | `JSON_VALUE()` / `JSON_QUERY()` | `->` / `->>` | `JSON_EXTRACT()` / `->` / `->>` |
744
+| 日期格式化 | `CONVERT(VARCHAR, date, style)` | `TO_CHAR()` | `DATE_FORMAT()` |
745
+
746
+**建议**:SQL Server 2019 是长期支持版本(LTSC),功能稳定,适合生产环境。
747
+
748
+
749
+
750
+
751
+
752
+## 六、快速迁移检查清单
753
+
754
+从 MySQL/PostgreSQL 迁移到 SQL Server 时,请逐项检查:
755
+
756
+- [ ] **标识符**:反引号 `` ` `` → 中括号 `[]`
757
+- [ ] **分页**:`LIMIT offset, count` → `OFFSET ... FETCH`
758
+- [ ] **日期函数**:`NOW()` → `GETDATE()`,`DATE_ADD` → `DATEADD`
759
+- [ ] **字符串连接**:`CONCAT()` / `||` → `+` / `CONCAT()`
760
+- [ ] **自增列**:`AUTO_INCREMENT` / `SERIAL` → `IDENTITY(1,1)`
761
+- [ ] **布尔值**:`BOOLEAN` / `TINYINT(1)` → `BIT`
762
+- [ ] **Unicode**:中文场景使用 `NVARCHAR` 替代 `VARCHAR`
763
+- [ ] **系统表**:优先使用 `sys.*` 视图而非 `INFORMATION_SCHEMA`
764
+- [ ] **扩展属性**:使用 `sp_addextendedproperty` 存储元数据
765
+- [ ] **UPDATE 别名**:使用 `FROM` 子句语法
766
+
767
+---
768
+
769
+**文档版本**: v2.0
770
+**最后更新**: 2026-03-24
771
+**适用版本**: SQL Server 2019 (15.x) / MySQL 8.0+ / PostgreSQL 14+
... ...
\ No newline at end of file