所以我遇到了一个实例,其中处理特定表上的更新的存储过程什么也没做,因为记录不存在[duh]。
所以我决定将存储过程更改为:
UPDATE Table
SET col1 = @var1
WHERE Type = @type AND UserId = @userId
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO Table
---etc....
END
所以这显然解决了这个问题,我将sproc从UpdateTable重命名为SetTable。
这是一个好主意,还是应该在应用程序级别执行某些操作,如果更新返回0行受影响,请调用插入过程。
答案 0 :(得分:3)
我更喜欢你所展示的“upsert”,因为它让我能够远离与持久性相关的逻辑。在应用程序中,我只想使用我的对象和“如果我必须”要求他们保存/持久化。通过单独更新和插入,您不得不在域模型中考虑这一点。但持久性无知的最大优势在于您可以专注于实际业务问题而不是基础架构问题。
这个设计也帮助我在今年早些时候迁移到ORM,因为我没有大量的“更新”/“插入”调用,而是一个简单的“保存”
答案 1 :(得分:2)
我喜欢每人一个。我让sproc执行一个特定的任务而不是两个任务,使其更容易维护和扩展。
答案 2 :(得分:2)
如果这与应用程序所期望的一致,那就没问题了。
如果业务意图只是为了更新现有记录,或者如果应用程序期望该行已经存在并且存储的proc默认隐藏了它没有的事实,那么它可能会破坏其他逻辑。如果另一个函数/进程因某个原因删除了行怎么办?如果行中没有更新的列有信息怎么办?插入可能会意外地更改这些列的值。
不是说这是事实。只有熟悉应用程序的人才能判断这种变化是否可以。
答案 3 :(得分:1)
它取决于。
在应用程序的某些地方,进行upsert是有意义的,因为你不知道或不关心当前保存的状态是否已经包含记录。然而,在其他地方,你会想要确切地知道插入记录的存在或更新丢失的记录应该是一个错误。
无论如何,你决定做'upsert',你必须小心,因为固有的竞争条件。两个单独的连接可能会尝试更新,两者都看到@@ ROWCOUNT为0,然后两者都尝试插入,导致主键违规(如果你很幸运)或更糟糕的数据库状态不一致(具有不同信息的重复记录)。正确地进行此类操作非常棘手。您必须确保UPDATE锁定状态(即没有其他事务可以插入)或者是红外线以在插入时遇到PK违规并相应地处理它。我喜欢后面的选项,因为竞争条件窗口通常非常小,处理异常比防止插入插件更容易。
要考虑的一个途径是使用新的SQL Server 2008 MERGE语句,它旨在处理此类情况(并最终赶上SQL Server与其他供应商,Oracle和MySQL已提供此功能多年。 ..)。
答案 4 :(得分:1)
我喜欢你的upsert(尽管不是存储过程:))但是,为什么逻辑上的方法与确定是否需要更新或插入无关 - 只需让sproc弄明白。< / p>