在 SQL Server 上插入更新存储过程
我编写了一个存储过程,如果记录存在,它将执行更新,否则它将执行插入。 它看起来像这样:
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
我以这种方式编写它背后的逻辑是,更新将使用 where 子句执行隐式选择,如果返回 0,则将发生插入。
这种方式的替代方法是进行选择,然后根据返回的行数进行更新或插入。 我认为这是低效的,因为如果你要进行更新,它将导致 2 个选择(第一个显式选择调用,第二个隐式在更新位置)。 如果进程要执行插入操作,那么效率上不会有任何差异。
我的逻辑合理吗? 这就是您将插入和更新组合到存储过程中的方式吗?
I've written a stored proc that will do an update if a record exists, otherwise it will do an insert. It looks something like this:
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
My logic behind writing it in this way is that the update will perform an implicit select using the where clause and if that returns 0 then the insert will take place.
The alternative to doing it this way would be to do a select and then based on the number of rows returned either do an update or insert. This I considered inefficient because if you are to do an update it will cause 2 selects (the first explicit select call and the second implicit in the where of the update). If the proc were to do an insert then there'd be no difference in efficiency.
Is my logic sound here?
Is this how you would combine an insert and update into a stored proc?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您的逻辑似乎是合理的,但如果您传入了特定的主键,您可能需要考虑添加一些代码以防止插入。
否则,如果您总是在更新不影响任何记录的情况下进行插入,那么当有人在您运行“UPSERT”之前删除该记录时会发生什么? 现在您尝试更新的记录不存在,因此它将创建一条记录。 这可能不是您正在寻找的行为。
Your logic seems sound, but you might want to consider adding some code to prevent the insert if you had passed in a specific primary key.
Otherwise, if you're always doing an insert if the update didn't affect any records, what happens when someone deletes the record before you "UPSERT" runs? Now the record you were trying to update doesn't exist, so it'll create a record instead. That probably isn't the behavior you were looking for.
修改了 Dima Malenko 帖子:
您可以捕获错误并将记录发送到失败的插入表。
我需要这样做,因为我们正在获取通过 WSDL 发送的所有数据,并在可能的情况下在内部修复它。
Modified Dima Malenko post:
You can trap the error and send the record to a failed insert table.
I needed to do this because we are taking whatever data is send via WSDL and if possible fixing it internally.
UPSERT 的忠实粉丝,确实减少了需要管理的代码。 这是我的另一种方法:输入参数之一是 ID,如果 ID 为 NULL 或 0,则说明这是一个 INSERT,否则是一个更新。 假设应用程序知道是否有 ID,因此不会在所有情况下都工作,但如果知道的话会将执行次数减少一半。
Big fan of the UPSERT, really cuts down on the code to manage. Here is another way I do it: One of the input parameters is ID, if the ID is NULL or 0, you know it's an INSERT, otherwise it's an update. Assumes the application knows if there is an ID, so wont work in all situations, but will cut the executes in half if you do.
如果您没有在 SQL 2008 中进行合并,则必须将其更改为:
if @@rowcount = 0 and @@error=0
否则,如果更新由于某种原因失败,那么它将尝试随后插入,因为失败语句为 0
If you are not doing a merge in SQL 2008 you must change it to:
if @@rowcount = 0 and @@error=0
otherwise if the update fails for some reason then it will try and to an insert afterwards because the rowcount on a failed statement is 0
请阅读我的博客上的帖子以获得良好、安全的模式您可以使用。 有很多考虑因素,并且关于这个问题的公认答案远非安全。
要快速获得答案,请尝试以下模式。 它在 SQL 2000 及以上版本上可以正常工作。 SQL 2005 为您提供错误处理,从而打开其他选项,而 SQL 2008 为您提供 MERGE 命令。
Please read the post on my blog for a good, safe pattern you can use. There are a lot of considerations, and the accepted answer on this question is far from safe.
For a quick answer try the following pattern. It will work fine on SQL 2000 and above. SQL 2005 gives you error handling which opens up other options and SQL 2008 gives you a MERGE command.
如果要与SQL Server 2000/2005一起使用,原始代码需要包含在事务中,以确保数据在并发情况下保持一致。
这会产生额外的性能成本,但会确保数据完整性。
添加,正如已经建议的,应在可用的情况下使用 MERGE。
If to be used with SQL Server 2000/2005 the original code needs to be enclosed in transaction to make sure that data remain consistent in concurrent scenario.
This will incur additional performance cost, but will ensure data integrity.
Add, as already suggested, MERGE should be used where available.
您的假设是正确的,这是执行此操作的最佳方法,称为 upsert/merge。
UPSERT 的重要性 - 来自 sqlservercentral.com:
编辑:
请查看此答案和链接的博客文章了解此模式的问题以及如何使其安全运行。
Your assumption is right, this is the optimal way to do it and it's called upsert/merge.
Importance of UPSERT - from sqlservercentral.com:
Edit:
Please check out this answer and the linked blog post to learn about the problems with this pattern and how to make it work safe.
顺便说一句,MERGE 是 SQL Server 2008 中的新功能之一。
MERGE is one of the new features in SQL Server 2008, by the way.
你不仅需要在事务中运行它,还需要高隔离级别。 事实上,默认隔离级别是已提交读,并且此代码需要可序列化。
也许添加@@错误检查和回滚可能是个好主意。
You not only need to run it in transaction, it also needs high isolation level. I fact default isolation level is Read Commited and this code need Serializable.
Maybe adding also the @@error check and rollback could be good idea.