T-SQL插入或更新

时间:2010-02-16 20:08:21

标签: sql-server tsql insert-update

我对SQL Server的性能有疑问。

假设我有一个包含以下列的表personsidnamesurname

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

  1. 如果表格中没有id,请插入该行。

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

  3. 我在这里有两个解决方案:

    首先:

    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。

5 个答案:

答案 0 :(得分:11)

选项1似乎很好。但是,如果您使用的是SQL Server 2008,则还可以使用MERGE,这可能对此类UPSERT任务有效。

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

答案 1 :(得分:8)

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

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

答案 2 :(得分:4)

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

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

答案 3 :(得分: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

namesurname必须可以为空。

交易意味着没有其他用户会看到“空白”记录。

编辑:清理

答案 4 :(得分:2)

您可以使用@@ RowCount查看更新是否有效。类似的东西:

    UPDATE MyTable
       SET SomeData = 'Some Data' WHERE ID = 1
    IF @@ROWCOUNT = 0
      BEGIN
        INSERT MyTable
        SELECT 1, 'Some Data'       
      END