create table stock(material int,quantity int)--库存表 insert stock select 1,100 union all select 2,20 create table stock_out(id int identity,material int,quantity int)--出库单表 go --出库表插入触发器,用于检查可用库存,并更新库存表减库存。 create trigger tr_stock_out_i on stock_out for insert as begin set nocount on if exists(select 1 from inserted _i join stock _s with(updlock) on _s.material = _i.material where _i.quantity > _s.quantity ) begin raiserror('库存不够!',16,1) rollback tran return end else begin waitfor delay '00:00:05'--模拟并发事务 update _s set _s.quantity = _s.quantity - _i.quantity from stock _s join inserted _i on _i.material = _s.material end
end go --如果不加锁,在两个连接中分别执行 waitfor time '15:12' insert stock_out(material,quantity) select 1,100 --然后 select * from stock where material = 1 --得 /* material quantity ----------- ----------- 1 -100
库存出现负数,因为sql执行的顺序为
连接1检查库存,满足 连接2检查库存,满足 连接1减库存,减到0 连接2减库存,减到-100
检查库存与减库存这两个动作之间必须是无缝的,所以在检查库存时要加更新锁。 更新锁是自排斥的,并且与共享锁兼容,它会保持到事务结束即insert语句最外层的事务结束。 */ go drop table stock,stock_out