为什么需要事务?
假定钱从A转到B,至少需要两步:
1.A的资金减少
2.然后B的资金相应增加
例子
先建立一个表,再增加一个约束,插入两条数据
create table bank
(
cId char(4) primary key,
balance money
)
alter table bank
add constraint CH_balance check(balance>=10)
go
insert into bank values('0001',1000)
insert into bank values('0002',10)
go
做好准备工作之后,执行下面两条语句,来完成转账
update bank set balance=balance-1000 where cid='0001'
update bank set balance=balance+1000 where cid='0002'
执行的时候会报一个错误,因为第一条语句执行完之后会为0,和约束冲突
,所以不会执行成功
又因为第一条的错误是运行时错误,所以仍然会接着执行下面的语句。
并且第二条语句不和约束冲突,所以会正常执行。
现在的情况是0001的钱没有扣除,而002的钱增加了,这样的情况在银行是绝对不让发生的。
解决办法:让这两句话作为一个整体来执行,要么都成功,要么都失败
事务的四个特征
原子性
原子性:一个事务对数据库的所有操作,要么全部执行,要么什么也不做。
隔离性
隔离性:多个事务并发执行时,保证执行结果是正确的。比如我在修改数据的时候,不允许别人读取。
可以做这样一个实验
我先在当前的SSMS执行
begin transaction
delete from bank;
然后再开启一个SSMS执行
select * from bank
执行所花时间一直在增长,没有结果,也没有消息。此时这个进程在等待另一个进程操作完成。
一旦我在第一个SSMS执行
rollback
第二个SSMS会立刻执行,查询出结果。
第一个SSMS中的事务执行的时候锁住了这张表,第二个SSMS中的语句无法操作这张表,只能等待。
封锁对象的大小(一行数据或一张表或。。。)称为封锁的粒度。
封锁的粒度越大,并发度也就越小,系统的开销也就越小。
持久性
持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
一致性
一致性:保证数据的完整性(说白了就是保证数据是正确的,啥是正确的,是由所建立的那些约束所限定的,符合这些约束的数据才是正确的,这些约束又是由现实世界中对象的特征来制定的)
数据的完整性是数据满足数据库描述对象的实际意义。
DBMS必须提供一种功能来保证数据库中的数据是正确的,避免非法的不符合语义的错误数据的输入与输出。检查数据库中的数据是否完整地条件称为“完整性检查”。数据库中数据应该满足的条件称为“完整性的约束条件”。
DBMS中执行完整性检查的子系统称为“完整性子系统”。其功能为:
1.监督事务的执行,并测试是否违反完整性规则
2.若有违反现象,则采取恰当的操作,譬如拒绝操作、报告违反情况、改正错误等方法来处理。
比如,现实世界中年龄不会小于0,于是定义了一个约束字段age要大于0。
这样数据库中的年龄应该都符合这个约束。
满足这四个特征的操作叫做事务。
事务指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)–也就是由多个sql语句组成,必须作为一个整体执行
这些sql语句作为一个整体一起向系统提交,要么都执行、要么都不执行。
隐式事务,就是平时写的sql语句,每一条语句都是一个事务
显式事务,手动将多条语句打包成一个事务
在数据库中所有的执行单元都是事务
事务是最小的执行单元
事务语法
开启事务(begin transaction,可简写为begin tran)
一般定义一个局部变量记录信息
书写T-SQL语句执行代码,并记录数据
判断记录的信息是否满足要求
满足提交事务(commit transaction,可简写为commit)
不满足回滚(rollback transaction,可简写为rollback)
开启事务,begin tran,从这句话开始,其后所有的代码都是这个事务中的操作,直到出现提交或回滚操作。
提交事务,commit,就是告诉数据库从begin tran开始执行的代码全部生效。
回滚,rollback,就是告诉数据库刚才的代码不算,操作回到执行之前的状态。
使用事务
先将数据恢复到原来的
然后执行
begin tran
begin try
update bank set balance=balance-1000 where cid='0001';
update bank set balance=balance+1000 where cid='0002';
commit;
end try
begin catch
rollback;
end catch
显示结果为
查看表中的数据
问题解决了,但是怎么在c#中知道是否执行成功,怎么用c#操作呢?可以把这些sql语句都写在c#里,但那样代码量太了,可以用存储过程,在下一篇博客里。