【问题标题】:MS SQL equivalent of ON DUPLICATE KEY UPDATE / UPSERTMS SQL 等效于 ON DUPLICATE KEY UPDATE / UPSERT
【发布时间】:2017-06-12 10:56:50
【问题描述】:

我是一个 postgres 用户,是 MS SQL 的新手。我需要复制 ON DUPLICATE KEY UPDATE 功能(有时称为 UPSERT)。我有一张包含年龄和性别的用户表(此处已简化)。

使用此示例查询,但根据需要更改 id,UPDATE 功能有效,但 INSERT 无效。没有错误,它只是说 0 行受到影响。

MERGE
    users AS target 
USING
    (SELECT id FROM users WHERE id=222) AS source
ON
    target.id = source.id

WHEN MATCHED THEN 
    UPDATE SET 
        target.id  = source.id,
        target.age = 33,
        target.sex = 'M'

WHEN NOT MATCHED THEN 
    INSERT (id, age, sex) VALUES (222, 33, 'M')
;

如果它很重要(也许有一些更简单的方法),我在 linux 中使用 python3。

附:我在 StackOverflow 中查看了其他 UPSERT in MSSQL 问题。这就是我得到这个语法的方式。不过,我无法通过他们理解这里的问题。

【问题讨论】:

  • 如果 id 只匹配 update 而不是 insert。

标签: sql sql-server upsert on-duplicate-key


【解决方案1】:

在 ASP.NET 中使用绑定参数来更新购物车中的产品的示例:

MERGE 
INTO [shoppingcart] WITH (holdlock) AS target 
using        ( 
                    SELECT @ProductID AS ProductID) AS source 
ON ( 
                          target.productid = source.productid) 
WHEN matched THEN 
UPDATE 
SET              target.productid = source.productid, 
                 target.quantity = target.quantity + 1, 
                 target.date = @Date, 
                 target.clientid = @ClientID 
WHEN NOT matched THEN 
INSERT 
       ( 
              quantity, 
              date, 
              clientid, 
              productid 
       ) 
       VALUES 
       ( 
              @Quantity, 
              @Date, 
              @ClientID, 
              @ProductID 
       );

【讨论】:

    【解决方案2】:

    我最终使用了来自 this question 的一些信息来解决它。它并没有完全解决它,但它帮助我看到了我的 subselect (...) AS 源的问题。基本上,(显然) USING 隐式假定源表,并通过使用FROM users WHERE ... 显式指定它,我阻止了 sql server 检查它。无论如何,这是我最好的理解。

    关键是,这个查询有效:运行一次,它会插入一个带有(555, 55, 'M') 的新用户。再次运行,同一条记录更新为(555, 22, 'F')。

    此外,显然 MERGE 可能会出现高并发问题,因此链接的问题建议使用 HOLDLOCK,我在这里。

    MERGE INTO users WITH (HOLDLOCK) AS target
        USING 
            (SELECT 555 AS id) AS source 
    ON 
        (target.id = source.id)
    
    WHEN MATCHED THEN 
            UPDATE SET 
                target.id  = source.id,
                target.age = 22,
                target.sex = 'F'
    WHEN NOT MATCHED THEN 
            INSERT (id, age, sex) VALUES (555, 55, 'M')
    ;
    

    【讨论】:

      【解决方案3】:

      在将表格合并到自身时,您将无法插入表格中不存在的行:

      我建议这样做:

      DECLARE @id INT = 222, @age int = 33, @sex VARCHAR(10) = 'M'
      
      
      IF EXISTS (SELECT id FROM users WHERE id=@id)
      BEGIN 
          UPDATE users SET age = @age, sex = @sex
      END
      ELSE 
      BEGIN
          INSERT INTO users (id, age, sex) VALUES (@id, @age, @sex)
      END
      

      如果您从另一个应用程序调用它,您可能需要一个存储过程,因此您可能需要创建一个存储过程来执行此操作:

      CREATE PROCEDURE sp_UpdateInsertUsers @id  INT 
                                   , @age INT 
                                   , @sex VARCHAR(10) 
      AS
          BEGIN
              SET NOCOUNT ON;
              IF EXISTS
                     (SELECT id
                      FROM   users
                      WHERE  id = @id)
                 BEGIN
                    UPDATE users
                      SET  age = @age, sex = @sex;
                 END;
              ELSE
                 BEGIN
                    INSERT INTO      users(id
                                     , age
                                     , sex)
                    VALUES
                          (@id
                         , @age
                         , @sex);
                 END;
          END;
      GO
      

      像这样调用过程:

      EXECUTE sp_UpdateInsertUsers @id  = 222
                                   , @age = 33
                                   , @sex = 'M'
      

      【讨论】:

      • 不幸的是,没有变化。不匹配时仍然(0 行受影响)。
      • 所以你正在将一个表合并到自身上?如果你有一个现有的密钥,那么你更新,但如果不存在这样的记录,那么你想插入?如果后者是正确的,那么我认为您不需要合并。
      • 如果您有另一个解决方案,我愿意接受 :) 我只需要插入一条记录,或者如果该记录的 id 已存在则更新。
      • 感谢您的帮助。我找到了一个更短的解决方案。我不完全确定它起作用的根本原因,但它的形式对我来说是可以理解的。
      • 你能分享一下这个解决方案吗?我很想知道它是什么
      猜你喜欢
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 2015-01-20
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-20
      相关资源
      最近更新 更多