认识 SQL Server 的 T-SQL 语法
T-SQL(Transact-SQL)是微软在标准 SQL 之上扩展的"SQL Server 母语"------它既继承了 SQL 声明式的查询能力,又加入了过程式编程的变量、流程控制、错误处理,让 DBA 和开发能在一个语言里完成"查数据 + 写逻辑 + 控事务"。很多人写了多年 T-SQL 却没系统梳理过它的语法全貌。本文用一篇讲透 T-SQL 的分类、过程式扩展、批次机制与高频语法点。

一句话总结
T-SQL = 标准 SQL(DDL / DML / DCL / TCL)+ 过程式扩展(变量 / 流程控制 / 错误处理 / 批次)+ SQL Server 专属语法甜点(TOP / OUTPUT / CTE / PIVOT / APPLY / STRING_AGG ...)。理解这三层,就能从"会写 SELECT"进阶到"会写存储过程、会控事务、会用 SQL Server 的特性提效"。
一、T-SQL 到底是什么?
1.1 从标准 SQL 到 T-SQL
SQL 是 ANSI/ISO 标准化的查询语言,但各数据库厂商都做了"方言扩展"。SQL Server 的方言就是 T-SQL(Transact-SQL)。
SQL(ANSI 标准)
│
│ 厂商扩展
▼
┌─────────────────────────────────────┐
│ T-SQL = 标准 SQL + 过程式编程扩展 │
│ + SQL Server 专属语法 │
└─────────────────────────────────────┘
| 对比项 | 标准 SQL | T-SQL |
|---|---|---|
| 基本查询 | ✅ 支持 | ✅ 支持 |
| 变量声明 | ❌ 没有 | ✅ DECLARE @x INT |
| 流程控制 | ❌ 没有 | ✅ IF/WHILE/CASE |
| 错误处理 | ❌ 没有 | ✅ TRY...CATCH |
| 批次分隔 | ❌ 没有 | ✅ GO |
TOP 取前 N 行 |
❌(标准用 FETCH) |
✅ 原生支持 |
OUTPUT 子句 |
❌ 没有 | ✅ 原生支持 |
一句话:标准 SQL 告诉你"要什么数据",T-SQL 还能告诉你"按什么逻辑处理数据"。
1.2 T-SQL 的三大组成
T-SQL
├── 1. 标准 SQL 语句(声明式)── DDL / DML / DCL / TCL
├── 2. 过程式扩展(命令式)── 变量 / 控制流 / 错误处理 / 批次
└── 3. 函数与语法甜点 ── 内置函数 / TOP / CTE / PIVOT / APPLY ...
二、四大语句分类(标准 SQL 部分)
T-SQL 继承了标准 SQL 的四类语句,这是 T-SQL 的地基。
| 类别 | 全称 | 作用 | 典型语句 |
|---|---|---|---|
| DDL | Data Definition Language | 定义结构 | CREATE / ALTER / DROP |
| DML | Data Manipulation Language | 操作数据 | SELECT / INSERT / UPDATE / DELETE / MERGE |
| DCL | Data Control Language | 权限控制 | GRANT / REVOKE / DENY |
| TCL | Transaction Control Language | 事务控制 | BEGIN TRAN / COMMIT / ROLLBACK / SAVE |
2.1 DDL:定义结构
sql
-- 建表
CREATE TABLE dbo.Employee (
EmpID INT IDENTITY(1,1) PRIMARY KEY, -- 自增主键
EmpName NVARCHAR(50) NOT NULL,
DeptID INT NOT NULL,
Salary DECIMAL(10,2),
HireDate DATE DEFAULT GETDATE(),
CONSTRAINT FK_Emp_Dept FOREIGN KEY (DeptID) REFERENCES dbo.Department(DeptID)
);
-- 加列
ALTER TABLE dbo.Employee ADD Email NVARCHAR(100);
-- 改列类型
ALTER TABLE dbo.Employee ALTER COLUMN Salary DECIMAL(12,2);
-- 删表
DROP TABLE dbo.Employee;
2.2 DML:操作数据
sql
-- 插入:VALUES 多行 / SELECT 子查询 / 输出
INSERT INTO dbo.Employee (EmpName, DeptID, Salary)
VALUES ('张三', 1, 12000),
('李四', 2, 15000);
INSERT INTO dbo.Employee (EmpName, DeptID, Salary)
SELECT Name, DeptID, Pay FROM dbo.HR_Stage;
-- 更新(带 FROM 联接是 T-SQL 特色)
UPDATE e
SET e.Salary = e.Salary * 1.1
FROM dbo.Employee e
JOIN dbo.Department d ON e.DeptID = d.DeptID
WHERE d.DeptName = N'研发部';
-- 删除
DELETE FROM dbo.Employee WHERE HireDate < '2020-01-01';
-- MERGE:一步实现"有则更新、无则插入"(upsert)
MERGE dbo.Employee AS t -- 目标表
USING (SELECT @Name AS EmpName, @DeptID AS DeptID) AS s -- 源数据
ON t.EmpName = s.EmpName
WHEN MATCHED THEN
UPDATE SET Salary = @Salary
WHEN NOT MATCHED THEN
INSERT (EmpName, DeptID, Salary) VALUES (s.EmpName, s.DeptID, @Salary);
2.3 DCL:权限控制
sql
GRANT SELECT, INSERT ON dbo.Employee TO role_app;
REVOKE INSERT ON dbo.Employee FROM role_app;
DENY DELETE ON dbo.Employee TO role_app; -- DENY 优先级最高
2.4 TCL:事务控制
sql
BEGIN TRAN;
UPDATE Account SET Balance = Balance - 500 WHERE AcctID = 'A';
UPDATE Account SET Balance = Balance + 500 WHERE AcctID = 'B';
COMMIT TRAN; -- 成功则提交
-- ROLLBACK TRAN; -- 失败则回滚
-- SAVE TRAN sp1; -- 设置保存点,可部分回滚
事务的 ACID 特性就靠 TCL 来落地。生产环境更新必须包在事务里,否则中途失败会产生脏数据。
三、过程式扩展(T-SQL 的灵魂)
标准 SQL 只能声明"要什么",T-SQL 的过程式扩展让它能像编程语言一样"按逻辑处理"。这一层是 T-SQL 区别于裸 SQL 的核心。
3.1 变量
sql
-- 局部变量(@开头,批内有效)
DECLARE @Cnt INT = 0; -- 声明并赋初值
DECLARE @AvgSal DECIMAL(10,2);
SET @AvgSal = 12000; -- SET 赋值(推荐,单值)
SELECT @Cnt = COUNT(*) FROM dbo.Employee; -- SELECT 赋值(从查询取值)
PRINT '员工数:' + CAST(@Cnt AS NVARCHAR(10)); -- 输出到消息窗口
| 赋值方式 | 语法 | 特点 |
|---|---|---|
SET |
SET @x = 值 |
标准写法,单值赋值,推荐 |
SELECT |
SELECT @x = col FROM t |
从查询取值,多行时取最后一行 |
局部变量以
@开头;全局变量(系统变量)以@@开头,如@@ROWCOUNT、@@IDENTITY、@@VERSION。
sql
UPDATE dbo.Employee SET Salary = 15000 WHERE EmpID = 1;
SELECT @@ROWCOUNT AS 受影响行数; -- 上一语句影响的行数
SELECT @@IDENTITY AS 新自增ID;
SELECT @@VERSION AS 版本;
3.2 流程控制
BEGIN...END 块
sql
IF @Cnt > 0
BEGIN
PRINT '有数据';
SET @Msg = 'OK';
END
BEGIN...END相当于 C/Java 的{ },把多条语句打包成一个逻辑块。
IF...ELSE
sql
IF @Cnt = 0
PRINT '无数据';
ELSE IF @Cnt < 10
PRINT '少量';
ELSE
PRINT '大量';
WHILE 循环
sql
DECLARE @i INT = 1;
WHILE @i <= 10
BEGIN
PRINT '第 ' + CAST(@i AS VARCHAR) + ' 次';
SET @i = @i + 1; -- T-SQL 没有 i++,必须显式累加
IF @i = 5 BREAK; -- 跳出循环
IF @i = 3 CONTINUE; -- 跳过本次,进入下一次
END
CASE 表达式
sql
-- 列级条件映射
SELECT EmpName,
Salary,
CASE
WHEN Salary >= 20000 THEN N'高薪'
WHEN Salary >= 10000 THEN N'中薪'
ELSE N'起薪'
END AS 薪资档位
FROM dbo.Employee;
CASE是表达式不是语句,必须返回一个值,常用于 SELECT 列、ORDER BY、UPDATE 的 SET。
其他跳转
| 语句 | 作用 |
|---|---|
BREAK |
跳出循环 |
CONTINUE |
跳过本次循环剩余部分 |
RETURN |
退出当前批/过程,可返回整数值 |
GOTO label |
无条件跳转(不推荐,破坏可读性) |
WAITFOR DELAY '00:00:05' |
等待 5 秒 |
3.3 错误处理 TRY...CATCH
sql
BEGIN TRY
BEGIN TRAN;
-- 这里写可能出错的操作
INSERT INTO dbo.Employee (EmpName) VALUES (NULL); -- 违反 NOT NULL
COMMIT TRAN;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRAN; -- 有事务则回滚
PRINT N'错误号:' + CAST(ERROR_NUMBER() AS NVARCHAR(10));
PRINT N'严重度:' + CAST(ERROR_SEVERITY() AS NVARCHAR(10));
PRINT N'存储过程:'+ COALESCE(ERROR_PROCEDURE(), N'(无)');
PRINT N'行号:' + CAST(ERROR_LINE() AS NVARCHAR(10));
PRINT N'消息:' + ERROR_MESSAGE();
END CATCH;
| 错误函数 | 返回 |
|---|---|
ERROR_NUMBER() |
错误编号 |
ERROR_SEVERITY() |
严重级别(0-25) |
ERROR_MESSAGE() |
错误文本 |
ERROR_LINE() |
出错行号 |
ERROR_PROCEDURE() |
出错的模块名 |
生产环境所有写操作都要包进 TRY...CATCH,并在 CATCH 里回滚事务------这是防止脏数据的安全网。
3.4 批次与 GO
**批次(Batch)**是 T-SQL 一次提交执行的一组语句,GO 是批次分隔符(不是真正的 T-SQL 语句,由工具解析)。
sql
-- 批次 1:建表
CREATE TABLE #T1 (ID INT);
GO
-- 批次 2:建变量(@x 只在本批有效)
DECLARE @x INT = 1;
PRINT @x;
GO
-- 批次 3:@x 已失效,下面会报"未声明"
-- PRINT @x; -- ❌ Must declare the scalar variable "@x".
| 规则 | 说明 |
|---|---|
| 变量作用域 | 局部变量只在当前批次有效,跨批失效 |
GO 后数字 |
GO 5 表示把上一批重复执行 5 次 |
| 不能在批中间的语句位置换批 | 某些语句(如 CREATE VIEW/PROC)必须独占一个批 |
sql
-- GO 带次数:重复执行 3 次
INSERT INTO dbo.Log(Msg) VALUES ('init');
GO 3
GO是 SQL Server 工具(SSMS、sqlcmd)识别的分隔符 ,不是标准 SQL 语法,在程序代码(如 ADO.NET、JDBC)里发 SQL 时不能用GO。
四、T-SQL 语法甜点(SQL Server 专属特性)
这些是标准 SQL 没有或写法不同的特性,是 T-SQL 的"加分项"。
4.1 TOP:取前 N 行
sql
-- 取前 10 行
SELECT TOP 10 EmpName, Salary
FROM dbo.Employee
ORDER BY Salary DESC;
-- 取前 10%(按百分比)
SELECT TOP 10 PERCENT * FROM dbo.Employee;
-- WITH TIES:并列名次也带上
SELECT TOP 5 WITH TIES EmpName, Salary
FROM dbo.Employee
ORDER BY Salary DESC;
标准 SQL 用
OFFSET ... FETCH NEXT N ROWS ONLY实现分页,T-SQL 的TOP更简洁但只支持"取前 N",分页推荐用OFFSET/FETCH。
sql
-- 标准分页写法(2012+)
SELECT EmpName, Salary
FROM dbo.Employee
ORDER BY EmpID
OFFSET 20 ROWS -- 跳过 20 行
FETCH NEXT 10 ROWS ONLY; -- 取 10 行(第 3 页)
4.2 OUTPUT 子句:返回受影响的数据
sql
-- 删除并把删掉的数据记入日志
DELETE FROM dbo.Employee
OUTPUT deleted.EmpID, deleted.EmpName, GETDATE() INTO dbo.DeleteLog
WHERE HireDate < '2020-01-01';
-- 更新时看前后的值
UPDATE dbo.Employee
SET Salary = Salary * 1.1
OUTPUT inserted.EmpName, deleted.Salary AS 旧薪, inserted.Salary AS 新薪;
| 伪表 | 含义 |
|---|---|
inserted |
INSERT/UPDATE 后的新值 |
deleted |
DELETE/UPDATE 前的旧值 |
OUTPUT相当于"操作 + 审计"一步到位,常用于变更留痕 和触发器逻辑替代。
4.3 CTE:通用表表达式
sql
WITH HighPaid AS (
SELECT EmpName, Salary, DeptID
FROM dbo.Employee
WHERE Salary > 15000
),
DeptAvg AS (
SELECT DeptID, AVG(Salary) AS AvgSal
FROM dbo.Employee
GROUP BY DeptID
)
SELECT h.EmpName, h.Salary, d.AvgSal
FROM HighPaid h
JOIN DeptAvg d ON h.DeptID = d.DeptID;
递归 CTE 是 CTE 的王牌应用------查树形结构(如组织架构、物料 BOM):
sql
WITH OrgTree AS (
-- 锚点:顶层节点
SELECT EmpID, EmpName, ManagerID, 1 AS Lvl
FROM dbo.Employee
WHERE ManagerID IS NULL
UNION ALL
-- 递归成员:找下级
SELECT e.EmpID, e.EmpName, e.ManagerID, o.Lvl + 1
FROM dbo.Employee e
JOIN OrgTree o ON e.ManagerID = o.EmpID
)
SELECT REPLICATE(' ', Lvl-1) + EmpName AS 组织层级, Lvl
FROM OrgTree
OPTION (MAXRECURSION 100); -- 限制递归深度,默认 100,0 表示无限
4.4 PIVOT / UNPIVOT:行列转换
sql
-- 把"行转列":每个部门的各职位薪资汇总
SELECT DeptID, [开发] AS 开发薪资, [测试] AS 测试薪资, [产品] AS 产品薪资
FROM (
SELECT DeptID, Position, Salary FROM dbo.Employee
) AS src
PIVOT (
SUM(Salary) FOR Position IN ([开发], [测试], [产品])
) AS pvt;
-- UNPIVOT:列转行(反向)
SELECT DeptID, Position, Salary
FROM PivotedTable
UNPIVOT (
Salary FOR Position IN ([开发], [测试], [产品])
) AS up;
4.5 APPLY:联接表值函数/子查询
sql
-- CROSS APPLY:类似 INNER JOIN,对每行调用表值函数
SELECT e.EmpName, t.TopOrderID
FROM dbo.Employee e
CROSS APPLY (
SELECT TOP 3 OrderID
FROM dbo.Orders
WHERE CustID = e.EmpID
ORDER BY OrderDate DESC
) t;
-- OUTER APPLY:类似 LEFT JOIN,函数无结果也保留行
SELECT e.EmpName, t.OrderID
FROM dbo.Employee e
OUTER APPLY (...) t;
| APPLY 类型 | 行为 |
|---|---|
CROSS APPLY |
只返回右表有匹配的行(= INNER JOIN) |
OUTER APPLY |
右表无匹配时返回 NULL(= LEFT JOIN) |
APPLY比普通 JOIN 强在:右表可以是依赖左表当前行的子查询或表值函数,JOIN 做不到。
4.6 STRING_AGG 与其他字符串函数(2017+)
sql
-- 把一行行的名字拼成一列,用逗号分隔
SELECT DeptID,
STRING_AGG(EmpName, ',') WITHIN GROUP (ORDER BY EmpName) AS 团队成员
FROM dbo.Employee
GROUP BY DeptID;
| 字符串函数 | 作用 |
|---|---|
STRING_AGG(col, sep) |
拼接多值为一串(2017+) |
STRING_SPLIT(str, sep) |
拆分字符串为表(2016+) |
CONCAT(a, b, ...) |
自动处理 NULL 拼接 |
FORMAT(val, fmt) |
格式化(如 FORMAT(getdate(),'yyyy-MM-dd')) |
STUFF(str, start, len, newstr) |
在指定位置替换子串 |
五、内置函数分类速查
T-SQL 有 200+ 内置函数,按类型掌握最高效。
| 类别 | 作用 | 代表函数 |
|---|---|---|
| 聚合 | 多值汇总 | SUM / AVG / COUNT / MIN / MAX |
| 字符串 | 文本处理 | SUBSTRING / REPLACE / CHARINDEX / STRING_AGG |
| 日期时间 | 时间运算 | GETDATE / DATEADD / DATEDIFF / FORMAT / EOMONTH |
| 数学 | 数值计算 | ROUND / CEILING / FLOOR / ABS / POWER |
| 转换 | 类型转换 | CAST / CONVERT / TRY_CAST / PARSE |
| NULL 处理 | 空值兜底 | ISNULL / COALESCE / NULLIF |
| 系统 | 元信息 | @@ROWCOUNT / @@IDENTITY / SCOPE_IDENTITY / HOST_NAME |
| 窗口 | 分组排名 | ROW_NUMBER / RANK / DENSE_RANK / LEAD / LAG / SUM() OVER() |
5.1 窗口函数(高频面试点)
sql
-- 按部门排薪资名次
SELECT EmpName, DeptID, Salary,
ROW_NUMBER() OVER (PARTITION BY DeptID ORDER BY Salary DESC) AS 行号,
RANK() OVER (PARTITION BY DeptID ORDER BY Salary DESC) AS 排名,
DENSE_RANK() OVER (PARTITION BY DeptID ORDER BY Salary DESC) AS 稠密排名
FROM dbo.Employee;
-- 累计求和
SELECT EmpName, Salary,
SUM(Salary) OVER (ORDER BY EmpID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 累计薪
FROM dbo.Employee;
| 函数 | 并列时行为 |
|---|---|
ROW_NUMBER() |
1,2,3,4(不重复,必有唯一序号) |
RANK() |
1,2,2,4(并列后跳号) |
DENSE_RANK() |
1,2,2,3(并列后不跳号) |
5.2 NULL 兜底:ISNULL vs COALESCE
sql
SELECT ISNULL(Salary, 0) AS 薪1, -- SQL Server 专有,两参数
COALESCE(Salary, Bonus, 0) AS 薪2; -- 标准 SQL,可多参数,取第一个非空
COALESCE是标准函数、参数个数不限、可跨数据库;ISNULL只接两参但略快。写可移植代码用 COALESCE,追求极致性能用 ISNULL。
六、T-SQL 实战:一个完整的存储过程
把上面所有知识点揉进一个真实场景------"按部门发奖金":
sql
CREATE OR ALTER PROCEDURE dbo.usp_GrantBonus
@DeptID INT,
@RaiseRate DECIMAL(4,2) = 0.10, -- 默认涨 10%
@Affected INT OUTPUT -- 输出参数:影响行数
AS
BEGIN
SET NOCOUNT ON; -- 不返回受影响行数提示,减少网络流量
DECLARE @DeptName NVARCHAR(50);
SELECT @DeptName = DeptName FROM dbo.Department WHERE DeptID = @DeptID;
IF @DeptName IS NULL
BEGIN
RAISERROR('部门 %d 不存在', 16, 1, @DeptID); -- 16 级错误抛出
RETURN -1; -- 退出并返回错误码
END
BEGIN TRY
BEGIN TRAN;
UPDATE e
SET e.Salary = e.Salary * (1 + @RaiseRate)
FROM dbo.Employee e
WHERE e.DeptID = @DeptID;
SET @Affected = @@ROWCOUNT;
-- 用 OUTPUT 把变更留痕
INSERT INTO dbo.BonusLog (EmpID, OldSal, NewSal, OpTime)
SELECT EmpID, OldSal, NewSal, GETDATE()
FROM (
UPDATE e
SET e.Salary = e.Salary * (1 + @RaiseRate)
OUTPUT inserted.EmpID, deleted.Salary AS OldSal,
inserted.Salary AS NewSal
FROM dbo.Employee e
WHERE e.DeptID = @DeptID
) AS chg;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
PRINT '发放奖金失败:' + ERROR_MESSAGE();
RETURN ERROR_NUMBER();
END CATCH
PRINT '部门 [' + @DeptName + '] 共 ' + CAST(@Affected AS VARCHAR) + ' 人涨薪';
RETURN 0;
END
GO
-- 调用
DECLARE @N INT;
EXEC dbo.usp_GrantBonus @DeptID=1, @RaiseRate=0.15, @Affected=@N OUTPUT;
SELECT @N AS 影响人数;
这段代码同时用到:变量、IF、TRY/CATCH、事务、@@ROWCOUNT、OUTPUT、RAISERROR、RETURN、输出参数、GO 批分隔------是 T-SQL 语法能力的综合体检。
七、T-SQL 高频坑与最佳实践
| # | 坑点 / 建议 | 说明 |
|---|---|---|
| 1 | 变量跨批失效 | DECLARE @x 在 GO 后失效,跨批需重新声明或用临时表/参数传递 |
| 2 | SET vs SELECT 赋值 |
SELECT @x=col 多行时取最后一行且不报错,易踩坑;单值用 SET |
| 3 | SCOPE_IDENTITY vs @@IDENTITY |
触发器里又插了别的表时,@@IDENTITY 返回的是触发器表的 ID;用 SCOPE_IDENTITY() 更安全 |
| 4 | ISNULL vs COALESCE |
前者专有两参略快,后者标准可移植;注意 COALESCE 返回类型按优先级推导 |
| 5 | 字符串用 N 前缀 |
中文/Unicode 字符串字面量必须 N'张三',否则会乱码 |
| 6 | GO 不能放程序代码里 |
GO 是工具指令,ADO.NET/JDBC 里发 SQL 用 GO 会语法报错 |
| 7 | WITH 语句前必须加分号 |
CTE 前若还有语句,需 ;WITH ... 防止语法歧义 |
| 8 | TOP 不带 ORDER BY 不可靠 |
无 ORDER BY 的 TOP 行序不保证,生产环境必须加排序 |
| 9 | 写操作必包事务 + TRY/CATCH | 防止中途失败产生脏数据,CATCH 里先判 @@TRANCOUNT 再回滚 |
| 10 | SET NOCOUNT ON |
存储过程开头加它,减少 DONE_IN_PROC 消息,降低网络开销 |
字符串前缀 N 的坑
sql
-- ❌ 不加 N:中文可能乱码(非 Unicode 列时)
INSERT INTO t VALUES ('张三');
-- ✅ 加 N:明确 Unicode
INSERT INTO t VALUES (N'张三');
八、T-SQL 学习路线建议
入门 ─────────────── 进阶 ─────────────── 高阶
│ │ │
▼ ▼ ▼
SELECT/INSERT 事务+TRY/CATCH 窗口函数
WHERE/JOIN 变量+流程控制 递归CTE
GROUP BY 存储过程+函数 执行计划调优
ORDER BY CTE/APPLY 并行/批次模式
TOP OUTPUT/PIVOT 扩展事件追踪
| 阶段 | 目标 | 验证标准 |
|---|---|---|
| 入门 | 能写增删改查 | 独立完成多表 JOIN + 聚合查询 |
| 进阶 | 能写存储过程 | 能用事务+错误处理封装业务逻辑 |
| 高阶 | 能调优与排查 | 看懂执行计划,能用窗口/CTE 解决复杂分析 |
九、T-SQL 语法速查表
| 类别 | 关键字 | 一句话 |
|---|---|---|
| DDL | CREATE/ALTER/DROP/TRUNCATE |
建改删结构 |
| DML | SELECT/INSERT/UPDATE/DELETE/MERGE |
查改增删 |
| DCL | GRANT/REVOKE/DENY |
权限控制 |
| TCL | BEGIN/COMMIT/ROLLBACK/SAVE TRAN |
事务控制 |
| 变量 | DECLARE/SET/SELECT @@ |
声明与赋值 |
| 流程 | IF/WHILE/CASE/BEGIN-END |
条件与循环 |
| 跳转 | BREAK/CONTINUE/RETURN/GOTO/WAITFOR |
流程跳转 |
| 错误 | TRY...CATCH/RAISERROR/THROW |
异常处理 |
| 批次 | GO [n] |
批次分隔 |
| 取前N | TOP n [PERCENT] [WITH TIES] |
取前 N 行 |
| 输出 | OUTPUT inserted/deleted |
返回变更数据 |
| 临时表 | #t / ##t |
局部/全局临时表 |
| CTE | WITH ... AS (...) |
通用表表达式 |
| 行列转换 | PIVOT/UNPIVOT |
行列互转 |
| 联接 | CROSS/OUTER APPLY |
关联表值函数 |
| 窗口 | ROW_NUMBER/RANK/SUM OVER() |
分组排名累计 |
| 字符串 | STRING_AGG/STRING_SPLIT/CONCAT |
拼接与拆分 |
十、与标准 SQL / 其他方言对比
| 特性 | T-SQL (SQL Server) | PL/SQL (Oracle) | MySQL |
|---|---|---|---|
| 变量前缀 | @(局部)/@@(全局) |
无前缀 / v_ 命名 |
@ |
| 流程块 | BEGIN...END |
BEGIN...END; |
BEGIN...END |
| 错误处理 | TRY...CATCH |
EXCEPTION 块 |
DECLARE HANDLER |
| 取前 N | TOP n |
FETCH FIRST n ROWS / ROWNUM |
LIMIT n |
| 批分隔 | GO |
/ |
; |
| 自增列 | IDENTITY |
SEQUENCE/触发器 |
AUTO_INCREMENT |
| 存储过程 | CREATE PROC |
CREATE PROCEDURE |
CREATE PROCEDURE |
| 字符串拼接 | + / CONCAT |
` |
多方言 DBA 最大的困扰不是语法本身,而是细节差异 :取前 N 行 Oracle 用
ROWNUM、MySQL 用LIMIT、SQL Server 用TOP,写代码前先确认目标库的方言。
DBA 夜话 :T-SQL 的语法看着庞杂,拆开就三块------标准 SQL 打地基、过程式扩展盖楼、专属语法做装修 。很多人卡在"会写 SELECT 但写不出存储过程",差的不是语法,是没把事务、错误处理、批次这三样当成肌肉记忆。建议拿本文第六节那个存储过程当靶子,照着敲一遍、改一遍、调一遍------当你能闭眼写出"事务 + TRY/CATCH + OUTPUT + 输出参数"的组合,T-SQL 这门"母语"就算真正入门了。记住一句话:写 SQL 是声明"要什么",写 T-SQL 是掌控"怎么要"------前者是查询,后者是工程。