【问题标题】:T-SQL Insert or updateT-SQL 插入或更新
【发布时间】:2011-01-17 13:42:12
【问题描述】:

我有一个关于 SQL Server 性能的问题。

假设我有一个表persons,其中包含以下列:idnamesurname

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

  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。

【问题讨论】:

  • 不确定,但我会做一个 if ((COUNT(*) ) > 0) 然后更新第二个选项

标签: sql-server tsql insert-update


【解决方案1】:

选项 1 似乎不错。但是,如果您使用的是 SQL Server 2008,您也可以使用 MERGE,它可能对此类 UPSERT 任务表现良好。

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

【讨论】:

  • 从链接的文档中不清楚如何将其与VALUES 一起使用,而不是合并另一个表中的行。你能举个例子吗?
  • @z0r 您总是可以用 VALUES 替换(只读)表,使用以下代替表引用:(VALUES (1,'a'),(2,'b')) AS v(i,s)
【解决方案2】:

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

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

【讨论】:

  • 您好,链接失效了。但是你的帖子很有道理。它节省了一次额外的搜索。谢谢!
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

    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 列必须可以为空。

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

    编辑:清理

    【讨论】:

    • 优雅,但您还必须为表中的每个非空字段插入值。
    • 是的。我倾向于避免not null 的任何功能性使用。应用程序需要非常完善才能为重要字段上传空值
    • 我最终的解决方案是在 PHP 中为put 执行逻辑 - 尝试完整插入,如果失败并显示$sqlErrors[0][1] == 2627,我会进行完整更新
    • 不小心投了反对票,过了一会儿才意识到。想删除我的反对票,您可以对您的答案进行任何形式的编辑,以便我这样做吗?
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-29
      • 1970-01-01
      • 1970-01-01
      • 2014-01-03
      • 2012-06-16
      • 1970-01-01
      相关资源
      最近更新 更多