一、事务核心概念(必考简答题)
1. 事务定义
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作。多个操作作为一个整体向系统提交,要么全部执行成功,要么全部不执行(回滚),事务是不可分割的工作逻辑单元。
2. 事务四大特性(ACID 必背)
-
原子性(Atomicity):事务整体不可拆分,所有操作同时成功或同时失败回滚,无部分执行情况。
-
一致性(Consistency):事务执行前后,数据库数据始终保持合法、一致的状态,不会出现数据错乱。
-
隔离性(Isolation):多个并发事务相互独立、互不干扰,彼此隔离运行。
-
永久性(Durability):事务提交成功后,对数据库的修改永久生效,不会丢失。
3. 事务三大分类
(1)自动提交事务(默认模式)
SQL Server 默认机制,每条独立T-SQL语句都是一个单独事务,执行成功自动提交,失败自动回滚。
(2)显式事务(重点、最常用)
手动通过 begin transaction 开启事务,手动控制 commit 提交、rollback 回滚,适用于多语句联动业务(转账、增减库存等)。
(3)隐性事务
开启开关 SET IMPLICIT_TRANSACTIONS ON 后,后续所有T-SQL语句自动开启事务,需手动提交/回滚,无需手动写开启语句。
二、无事务的业务BUG(反面案例)
1. 创建银行卡表+测试数据
sql
-- 创建银行卡资金表(带余额校验:余额必须大于1,规范字段类型)
create table CardNew
(
StudentId int identity(1000,1) primary key not null,
StudentName varchar(10) not null,
CurrentMoney money check(CurrentMoney > 1)
)
-- 初始化测试数据
insert into CardNew(StudentName,CurrentMoney) values('赵云',1400)
insert into CardNew(StudentName,CurrentMoney) values('刘备',1500)
insert into CardNew(StudentName,CurrentMoney) values('关羽',1600)
insert into CardNew(StudentName,CurrentMoney) values('曹操',1700)
-- 【BUG场景】无事务转账(典型数据错乱问题)
-- 1001刘备余额1500,转出1000后剩余500(符合check约束,可执行)
update CardNew set CurrentMoney=CurrentMoney-1000 where StudentId=1001
-- 1002关羽转入1000
update CardNew set CurrentMoney=CurrentMoney+1000 where StudentId=1002
-- 查看数据
select * from CardNew
-- 【手动制造报错场景】重新初始化数据,演示BUG
truncate table CardNew
insert into CardNew(StudentName,CurrentMoney) values('赵云',1400)
insert into CardNew(StudentName,CurrentMoney) values('刘备',1500)
insert into CardNew(StudentName,CurrentMoney) values('关羽',1600)
insert into CardNew(StudentName,CurrentMoney) values('曹操',1700)
-- 错误场景:1001转出1500,余额=0,违反 CurrentMoney>1 约束
update CardNew set CurrentMoney=CurrentMoney-1500 where StudentId=1001
-- 本条正常执行,导致:转出失败、转入成功,数据不一致
update CardNew set CurrentMoney=CurrentMoney+1500 where StudentId=1002
select * from CardNew
BUG原因:默认自动提交事务,两条update语句相互独立,前一句报错回滚,后一句正常提交,导致数据不一致。
三、基础显式事务(@@ERROR 错误捕获)
通过系统全局变量 @@ERROR 累计错误数,判断是否回滚/提交事务,解决转账数据错乱问题。
sql
-- 重置测试数据
truncate table CardNew
insert into CardNew(StudentName,CurrentMoney) values('赵云',1400)
insert into CardNew(StudentName,CurrentMoney) values('刘备',1500)
insert into CardNew(StudentName,CurrentMoney) values('关羽',1600)
insert into CardNew(StudentName,CurrentMoney) values('曹操',1700)
declare @errorNum int -- 定义错误计数器
set @errorNum=0
begin transaction -- 手动开启显式事务
-- 转出操作(余额清零,触发约束报错)
update CardNew set CurrentMoney=CurrentMoney-1500 where StudentId=1001
set @errorNum=@errorNum+@@ERROR -- 累加SQL错误码
-- 转入操作
update CardNew set CurrentMoney=CurrentMoney+1500 where StudentId=1002
set @errorNum=@errorNum+@@ERROR
-- 事务提交/回滚判断
if(@errorNum > 0)
begin
rollback transaction -- 任意步骤报错,整体回滚
print '事务执行失败,已全部回滚'
end
else
begin
commit transaction -- 无错误,正常提交
print '事务执行成功,数据已更新'
end
select * from CardNew
四、基础转账事务存储过程(可直接调用)
封装通用转账逻辑,支持自定义入账账号、出账账号、转账金额,适配所有转账场景。
sql
-- 存在则删除存储过程
if exists(select * from sysobjects where name='Test1')
drop proc Test1
go
-- 基础通用转账事务存储过程
create proc Test1
@inAccount int, -- 入账账号
@outAccount int, -- 出账账号
@jine int -- 转账金额
as
declare @errorNum int
set @errorNum=0
begin transaction -- 开启事务
-- 出账账号扣款
update CardNew set CurrentMoney=CurrentMoney-@jine where StudentId=@outAccount
set @errorNum=@errorNum+@@ERROR
-- 入账账号加款
update CardNew set CurrentMoney=CurrentMoney+@jine where StudentId=@inAccount
set @errorNum=@errorNum+@@ERROR
-- 事务逻辑判断
if(@errorNum > 0)
begin
rollback transaction
print '转账失败,事务回滚'
end
else
begin
commit transaction
print '转账成功,事务提交'
end
go
-- 测试1:正常转账(成功)
truncate table CardNew
insert into CardNew(StudentName,CurrentMoney) values('赵云',1400)
insert into CardNew(StudentName,CurrentMoney) values('刘备',1500)
insert into CardNew(StudentName,CurrentMoney) values('关羽',1600)
insert into CardNew(StudentName,CurrentMoney) values('曹操',1700)
exec Test1 1003,1000,300
select * from CardNew
-- 测试2:异常转账(触发约束,失败回滚)
exec Test1 1003,1000,1200
select * from CardNew
五、高级异常捕获:TRY-CATCH 语法
1. 基础TRY-CATCH异常捕获
用于捕获代码运行异常,避免程序中断,可手动抛出错误、终止执行。
sql
-- 基础 TRY-CATCH 异常捕获语法
begin try
-- 强制类型转换错误(模拟业务异常)
declare @i int
set @i = CAST('abc' as int)
print @i
end try
begin catch
-- 抛出自定义错误信息
raiserror('字符串无法转换为数字,数据类型错误',16,1)
print '代码执行异常,已捕获错误'
end catch
2. TRY-CATCH 结合事务
sql
-- TRY-CATCH + 事务 标准组合用法
begin tran
begin try
delete from StudentInfo where StudentId=1000
-- 主动抛出异常,测试回滚效果
raiserror('无此学生编号,删除失败',16,1)
commit tran -- 无异常则提交事务
print '执行成功,事务已提交'
end try
begin catch
print '代码执行出错,事务已回滚'
if(@@TRANCOUNT > 0)
rollback tran -- 存在未提交事务则回滚
end catch
核心逻辑:TRY包裹正常业务代码,CATCH捕获所有异常,只要出错立刻回滚事务,保证数据一致。
六、终极完整版转账存储过程(双重校验+业务判断+双异常捕获)
整合业务逻辑校验 + @@ERROR错误捕获 + TRY-CATCH异常捕获,企业级标准写法,无漏洞、安全性最高。
sql
-- 终极完整版:业务校验+事务+双重异常捕获 转账存储过程
if exists ( select * from sysobjects where name='test01')
drop proc test01
go
create proc test01
@inAccount int, -- 入账账号
@outAccount int, -- 出账账号
@jine int -- 转账金额
as
declare @errorNum int
set @errorNum=0
-- ========== 第一层:前置业务合法性校验(拦截逻辑错误) ==========
-- 1. 转账金额不能为负数
if (@jine <= 0)
begin
raiserror('转账金额不合法,必须大于0',16,1)
return
end
-- 2. 校验转出账号是否存在
if not exists (select 1 from CardNew where StudentId=@outAccount)
begin
raiserror('转出账户不存在',16,1)
return
end
-- 3. 校验转入账号是否存在
if not exists (select 1 from CardNew where StudentId=@inAccount)
begin
raiserror('转入账户不存在',16,1)
return
end
-- 4. 校验转出账户余额是否充足
declare @yue money
select @yue = CurrentMoney from CardNew where StudentId=@outAccount
if(@yue < @jine)
begin
raiserror('账户余额不足,无法转账',16,1)
return
end
-- ========== 第二层:事务 + 双重错误捕获(拦截系统SQL错误) ==========
begin tran -- 开启事务
begin try
-- 转出扣款
update CardNew set CurrentMoney=CurrentMoney-@jine where StudentId=@outAccount
set @errorNum=@errorNum+@@ERROR
-- 转入加款
update CardNew set CurrentMoney=CurrentMoney+@jine where StudentId=@inAccount
set @errorNum=@errorNum+@@ERROR
-- 捕获轻微SQL执行错误
if(@errorNum > 0)
begin
raiserror('SQL语句执行出错,事务回滚',16,1)
rollback transaction
end
else
begin
commit transaction
print '转账业务执行成功'
end
end try
begin catch
-- 捕获严重异常,防止事务悬挂
if(@@TRANCOUNT > 0)
begin
rollback tran
print '程序异常,事务强制回滚'
end
return
end catch
go
-- 测试调用
truncate table CardNew
insert into CardNew(StudentName,CurrentMoney) values('赵云',1400)
insert into CardNew(StudentName,CurrentMoney) values('刘备',1500)
insert into CardNew(StudentName,CurrentMoney) values('关羽',1600)
insert into CardNew(StudentName,CurrentMoney) values('曹操',1700)
-- 正常转账
exec test01 1001,1000,200
select * from CardNew
-- 异常测试(余额不足)
exec test01 1002,1000,2000
select * from CardNew
七、核心考点总结(考前必背)
-
事务作用:保证多步关联操作数据一致性,防止部分执行、数据错乱(转账核心场景)。
-
ACID四大特性:原子性、一致性、隔离性、永久性(简答题必考)。
-
事务三语句:begin transaction开启、commit提交、rollback回滚。
-
错误捕获区别:@@ERROR捕获单条SQL错误;TRY-CATCH捕获所有运行异常(严重错误)。
-
双层校验逻辑:先业务人工校验(金额、账号、余额),再系统事务校验,零漏洞。
-
@@TRANCOUNT:判断当前是否存在未提交的事务,避免重复回滚报错。