所以,尽管有一段时间做这个sql服务器的事情,我发现自己第二次猜测自己。
场景:我想卖东西,但我不想过度卖掉它。这是我的查询。 (@Quantity通常是负数,SalesItemId是表的主键)。这似乎是一个很好的解决方案
UPDATE SalesItem
SET QtyAvailable = QtyAvailable + @Quantity
WHERE SalesItemId = @Id
AND QtyAvailable + @Quantity >= 0
IF @@ROWCOUNT = 0 BEGIN
RAISERROR ('Cannot sell item, would sell out')
END
这会在同一个trasaction中进行更新和查询,从而防止QtyAvailable小于零吗?我本以为是这样,但我得到了我最终会销售更多物品的实例。我一直认为它是系统的另一部分,但所有的手指似乎都指向这个查询?
做两个阶段的过程会更好吗?例如。这(或更新前检查的相反)
BEGIN TRAN
UPDATE SalesItem
SET QtyAvailable = QtyAvailable + @Quantity
WHERE SalesItemId = @Id
IF ( SELECT QtyAvailable > FROM SalesItem where SalesItemId = @Id) < 0
BEGIN
RAISERROR ('Cannot sell item, would sell out')
ROLLBACK TEAN
RETURN
END
COMMIT TRAN
答案 0 :(得分:2)
您的单个更新语句是原子事务。它看起来既高效又优雅。如果您获得负QtyAvaliable值,则应检查代码中修改其值的所有其他实例。
你说通常@Quantity是一个负数。确保如果您有实例要添加正数,然后使用抵消交易减去它,这些交易将在交易中一起完成。
答案 1 :(得分:1)
恕我直言在数据库中隐藏业务逻辑是一个错误(留在应用程序中 - 更容易测试,更改,调试,编写等等),但如果你绝对必须(例如,没有“应用程序” - 你我正在使用一个使用SQL来处理事情的工具,或者你有很多应用依赖数据库完成工作的遗留情况,我会:
然后将原子性连接到插入(或更新或删除)语句,因为触发器/检查在与触发它们的事件相同的事务中触发。
简单地说,有了这个,任何订单数据更改查询都不可能导致存在负库存水平的情况。