【问题标题】:SQL Server 2005 implementation of MySQL REPLACE INTO?SQL Server 2005 执行 MySQL REPLACE INTO?
【发布时间】:2010-09-05 05:46:41
【问题描述】:

MySQL 有这个非常有用但专有的REPLACE INTO SQL 命令。

这可以在 SQL Server 2005 中轻松模拟吗?

开始一个新的事务,做一个Select() 然后UPDATEINSERTCOMMIT 总是有点痛苦,特别是在应用程序中这样做时,因此总是保留两个版本声明。

我想知道是否有一种简单且通用的方法可以在 SQL Server 2005 中实现这样的功能?

【问题讨论】:

    标签: mysql sql-server sql-server-2005


    【解决方案1】:

    我就这个问题写了blog post

    底线是,如果您想要便宜的更新并且想要安全地进行并发使用,请尝试:

    update t
    set hitCount = hitCount + 1
    where pk = @id
    
    if @@rowcount < 1 
    begin 
       begin tran
          update t with (serializable)
          set hitCount = hitCount + 1
          where pk = @id
          if @@rowcount = 0
          begin
             insert t (pk, hitCount)
             values (@id,1)
          end
       commit tran
    end
    

    这样,您有 1 次更新操作和最多 3 次插入操作。因此,如果您经常更新,这是一个安全且便宜的选择。

    我也会非常小心,不要使用任何对并发使用不安全的东西。在生产中很容易出现主键违规或重复行。

    【讨论】:

      【解决方案2】:

      这让我对 MSSQL (rant on my blog) 感到恼火。我希望 MSSQL 支持 upsert

      @Dillie-O 的代码在旧 SQL 版本中是一个好方法(+1 票),但它仍然基本上是两个 IO 操作(exists 然后updateinsert

      this post 上有一个稍微好一点的方法,基本上:

      --try an update
      update tablename 
      set field1 = 'new value',
          field2 = 'different value',
          ...
      where idfield = 7
      
      --insert if failed
      if @@rowcount = 0 and @@error = 0
          insert into tablename 
                 ( idfield, field1, field2, ... )
          values ( 7, 'value one', 'another value', ... )
      

      如果是更新,则将其减少到一个 IO 操作,如果是插入,则将其减少到两个。

      MS Sql2008 从 SQL:2003 标准引入merge

      merge tablename as target
      using (values ('new value', 'different value'))
          as source (field1, field2)
          on target.idfield = 7
      when matched then
          update
          set field1 = source.field1,
              field2 = source.field2,
              ...
      when not matched then
          insert ( idfield, field1, field2, ... )
          values ( 7,  source.field1, source.field2, ... )
      

      现在它实际上只是一个 IO 操作,但是代码很糟糕:-(

      【讨论】:

      • 太好了,谢谢!保存 Select 并且在我可以确定在 Update 和“我的”插入之间没有其他插入该键的情况下,通常甚至不需要 teransaction。
      • @Michael 如果要使用此解决方案,您最好在此表上有一个唯一索引并处理重复键错误。
      • @Keith 您的合并语句不起作用。 MERGE 不支持 WHERE 子句,您必须使用 USINGON 重写它。此外,除非您添加 WITH (HOLDLOCK),否则可能会出现竞争和并发 INSERTs,其中一个会因密钥冲突而失败。
      • 是的,正如这里所指出的:weblogs.sqlteam.com/dang/archive/2009/01/31/… MERGE 不是原子的。它取出一个隐式更新锁,但在执行插入之前释放它,这会导致可能导致主键违规的竞争条件。除了隐式 UPDLOCK 之外,您还必须使用显式 HOLDLOCK 才能使操作成为原子操作。就目前而言,它不是原子的,尽管它看起来是一个单一的语句。
      • MERGE 语法错误,已在同一作者的最新回答中修复:stackoverflow.com/a/243670/24472
      【解决方案3】:

      upsert/merge 正在做的事情是......

      IF EXISTS (SELECT * FROM [Table] WHERE Id = X)
         UPDATE [Table] SET...
      ELSE
         INSERT INTO [Table]
      

      所以希望这些文章和这个伪代码的结合可以让事情发生变化。

      【讨论】:

        【解决方案4】:

        您正在寻找的功能传统上称为 UPSERT。至少知道它的名称可能会帮助您找到所需的内容。

        我不认为 SQL Server 2005 有任何很好的方法来做到这一点。 2008 引入了 MERGE 语句,可用于完成此操作,如下所示:http://www.databasejournal.com/features/mssql/article.php/3739131http://blogs.conchango.com/davidportas/archive/2007/11/14/SQL-Server-2008-MERGE.aspx

        Merge 在 2005 年的测试版中可用,但他们在最终版本中将其删除。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-04
          • 1970-01-01
          • 1970-01-01
          • 2013-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多