这是原子更新吗?为什么方式更好

时间:2015-06-22 02:07:40

标签: sql-server

所以,尽管有一段时间做这个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

2 个答案:

答案 0 :(得分:2)

您的单个​​更新语句是原子事务。它看起来既高效又优雅。如果您获得负QtyAvaliable值,则应检查代码中修改其值的所有其他实例。

你说通常@Quantity是一个负数。确保如果您有实例要添加正数,然后使用抵消交易减去它,这些交易将在交易中一起完成。

答案 1 :(得分:1)

恕我直言在数据库中隐藏业务逻辑是一个错误(留在应用程序中 - 更容易测试,更改,调试,编写等等),但如果你绝对必须(例如,没有“应用程序” - 你我正在使用一个使用SQL来处理事情的工具,或者你有很多应用依赖数据库完成工作的遗留情况,我会:

  • 在更新产品库存水平的订单行项目的插入(和更新,删除)上创建触发器
  • 在产品上创建一个检查约束,要求粘贴级别为非零

然后将原子性连接到插入(或更新或删除)语句,因为触发器/检查在与触发它们的事件相同的事务中触发。

简单地说,有了这个,任何订单数据更改查询都不可能导致存在负库存水平的情况。