40

我对 SQL Server 的性能有疑问。

假设我有一个persons包含以下列的表格:id, name, surname.

现在,我想在这个表中插入一个新行。规则如下:

  1. 如果id表中不存在,则插入该行。

  2. 如果id存在,则更新。

我这里有两个解决方案:

第一的:

update persons
  set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
if @@ROWCOUNT = 0 
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

第二:

if exists (select id from persons where id = @p_id)
  update persons
    set id=@p_id, name=@p_name, surname=@p_surname
  where id=@p_id
else
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

什么是更好的方法?似乎在第二种选择中,要更新一行,必须搜索两次,而在第一种选择中 - 只需一次。有没有其他解决方案?我正在使用 MS SQL 2000。

4

5 回答 5

13

选项 1 似乎不错。但是,如果您使用的是 SQL Server 2008,您还可以使用MERGE,这对于此类 UPSERT 任务可能会执行得很好。

请注意,您可能希望对此类任务使用显式事务和XACT_ABORT选项,以便在出现问题或并发更改的情况下保持事务一致性。

于 2010-02-16T20:25:00.397 回答
12

两者都可以正常工作,但我通常使用选项 2 (pre-mssql 2008),因为它读起来更清楚一些。我也不会强调这里的性能......如果它成为一个问题,您可以NOLOCKexists子句中使用。虽然在你开始在任何地方使用 NOLOCK 之前,请确保你已经覆盖了所有的基础(索引和大图架构的东西)。如果您知道您将多次更新每个项目,那么考虑选项 1 可能是值得的。

选项 3 是不使用破坏性更新。这需要更多的工作,但基本上每次数据更改时都插入一个新行(从不更新或从表中删除)并有一个选择所有最新行的视图。如果您希望表包含其所有先前状态的历史记录,这很有用,但它也可能是矫枉过正。

于 2010-02-17T00:52:50.033 回答
5

我倾向于使用选项 1。如果表中有记录,则保存一次搜索。如果没有,你不会丢失任何东西。此外,在第二个选项中,您可能会遇到与锁不兼容相关的有趣锁定和死锁问题。我的博客上有更多信息:

http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

于 2010-02-16T23:58:14.967 回答
4

您可以只使用 @@RowCount 来查看更新是否有任何作用。就像是:

    UPDATE MyTable
       SET SomeData = 'Some Data' WHERE ID = 1
    IF @@ROWCOUNT = 0
      BEGIN
        INSERT MyTable
        SELECT 1, 'Some Data'       
      END
于 2017-05-09T10:24:15.070 回答
3

为了更干一点,我避免两次写出值列表。

begin tran
insert into persons (id)
select @p_id from persons
 where not exists (select * from persons where id = @p_id)

update persons
set name=@p_name, surname=@p_surname
where id = @p_id

commit

name并且surname必须可以为空。

该交易意味着其他用户将永远不会看到“空白”记录。

编辑:清理

于 2016-10-18T23:54:40.120 回答