在Sql Server中相当于MySQL ON DUPLICATE KEY UPDATE

时间:2014-11-22 10:29:42

标签: mysql sql sql-server

我试图在Sql Server(2012)中找到以下MySql查询的等价物?

INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES ( 'VAL_A','VAL_B', 'VAL_C', 'VAL_D')
ON DUPLICATE KEY UPDATE COL_D= VALUES(COL_D);

有人可以帮忙吗?

PS。我已经读过MERGE查询具有类似的功能,但我发现它的语法非常不同。

5 个答案:

答案 0 :(得分:18)

您基本上在寻找插入或更新模式,有时也称为 Upsert。

我建议:Insert or Update pattern for Sql Server - Sam Saffron

对于将处理单行的过程,这些事务都可以正常工作:

Sam Saffron的第一个解决方案(适用于此架构):

begin tran
if exists (
  select * 
    from mytable with (updlock,serializable) 
    where col_a = @val_a
      and col_b = @val_b
      and col_c = @val_c
  )
  begin
    update mytable
      set col_d = @val_d
      where col_a = @val_a
        and col_b = @val_b
        and col_c = @val_c;
  end
else
  begin
    insert into mytable (col_a, col_b, col_c, col_d)
      values (@val_a, @val_b, @val_c, @val_d);
  end
commit tran
Sam Saffron的第二个解决方案(适用于此架构):

begin tran
  update mytable with (serializable)
    set col_d = @val_d
      where col_a = @val_a
        and col_b = @val_b
        and col_c = @val_c;
  if @@rowcount = 0
    begin
        insert into mytable (col_a, col_b, col_c, col_d)
          values (@val_a, @val_b, @val_c, @val_d);
     end
commit tran

即使有创意使用 IGNORE_DUP_KEY ,您仍然不得不使用插入/更新块或合并声明。

update mytable
  set col_d = 'val_d'
  where col_a = 'val_a'
    and col_b = 'val_b'
    and col_c = 'val_c';

insert into mytable (col_a, col_b, col_c, col_d)
  select 'val_a','val_b', 'val_c', 'val_d'
  where not exists (select * 
    from mytable with (serializable) 
    where col_a = 'val_a'
      and col_b = 'val_b'
      and col_c = 'val_c'
      );

The Merge answer provided by Spock应该做你想做的事。

不一定建议使用合并。我用它,但我永远不会承认@AaronBertrand。

答案 1 :(得分:9)

试试这个...... 我添加了注释以尝试解释在SQL Merge语句中发生的情况。 资料来源:MSDN : Merge Statement

Merge语句与ON DUPLICATE KEY UPDATE语句的不同之处在于,您可以告诉它使用哪些列进行合并。

CREATE TABLE #mytable(COL_A VARCHAR(10), COL_B VARCHAR(10), COL_C VARCHAR(10), COL_D VARCHAR(10))
INSERT INTO #mytable VALUES('1','0.1', '0.2', '0.3'); --<These are the values we'll be updating

SELECT * FROM #mytable --< Starting values (1 row)

    MERGE #mytable AS target --< This is the target we want to merge into
    USING ( --< This is the source of your merge. Can me any select statement
        SELECT '1' AS VAL_A,'1.1' AS VAL_B, '1.2' AS VAL_C, '1.3' AS VAL_D --<These are the values we'll use for the update. (Assuming column COL_A = '1' = Primary Key)
        UNION
        SELECT '2' AS VAL_A,'2.1' AS VAL_B, '2.2' AS VAL_C, '2.3' AS VAL_D) --<These values will be inserted (cause no COL_A = '2' exists)
        AS source (VAL_A, VAL_B, VAL_C, VAL_D) --< Column Names of our virtual "Source" table
    ON (target.COL_A = source.VAL_A) --< This is what we'll use to find a match "JOIN source on Target" using the Primary Key
    WHEN MATCHED THEN --< This is what we'll do WHEN we find a match, in your example, UPDATE COL_D = VALUES(COL_D);
        UPDATE SET
            target.COL_B = source.VAL_B,
            target.COL_C = source.VAL_C,
            target.COL_D = source.VAL_D
    WHEN NOT MATCHED THEN --< This is what we'll do when we didn't find a match
    INSERT (COL_A, COL_B, COL_C, COL_D)
    VALUES (source.VAL_A, source.VAL_B, source.VAL_C, source.VAL_D)
    --OUTPUT deleted.*, $action, inserted.* --< Uncomment this if you want a summary of what was inserted on updated.
    --INTO #Output  --< Uncomment this if you want the results to be stored in another table. NOTE* The table must exists
    ;
SELECT * FROM #mytable --< Ending values (2 row, 1 new, 1 updated)

希望有所帮助

答案 2 :(得分:2)

存储过程将节省一天。

这里我假设COL_A和COL_B是唯一的列,是INT的类型 NB!没有sql-server实例ATM因此无法保证语法的正确性。 UPDATE!这是SQLFIDDLE

的链接
 CREATE TABLE mytable
(
COL_A int UNIQUE,
COL_B int UNIQUE,
COL_C int,
COL_D int,
)

GO

INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES (1,1,1,1),
(2,2,2,2),
(3,3,3,3),
(4,4,4,4);
GO

CREATE PROCEDURE updateDuplicate(@COL_A INT, @COL_B INT, @COL_C INT, @COL_D INT)
AS
BEGIN
    DECLARE @ret INT
    SELECT @ret = COUNT(*) 
    FROM mytable p 
    WHERE p.COL_A = @COL_A 
        AND p.COL_B = @COL_B

     IF (@ret = 0) 
        INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
        VALUES ( @COL_A, @COL_B, @COL_C, @COL_D)

     IF (@ret > 0)
        UPDATE mytable SET COL_D = @COL_D WHERE col_A = @COL_A AND COL_B = @COL_B  
END;
GO

然后使用所需的值而不是Update语句

调用此过程
exec updateDuplicate 1, 1, 1, 2
GO
SELECT * from mytable
GO

答案 3 :(得分:2)

您可以使用INSTEAD OF TRIGGER

模拟近乎相同的行为
CREATE TRIGGER tMyTable ON MyTable
INSTEAD OF INSERT
AS
    BEGIN
        SET NOCOUNT ON;

        SELECT i.COL_A, i.COL_B, i.COL_C, i.COL_D, 
            CASE WHEN mt.COL_D IS NULL THEN 0 ELSE 1 END AS KeyExists 
            INTO #tmpMyTable
            FROM INSERTED i
            LEFT JOIN MyTable mt
            ON i.COL_D = mt.COL_D;

        INSERT INTO MyTable(COL_A, COL_B, COL_C, COL_D)
            SELECT COL_A, COL_B, COL_C, COL_D
                FROM #tmpMyTable
                WHERE KeyExists = 0;

        UPDATE mt
            SET mt.COL_A = t.COL_A, mt.COL_B = t.COL_B, mt.COL_C = t.COL_C
            FROM MyTable mt 
                INNER JOIN #tmpMyTable t 
                ON mt.COL_D = t.COL_D AND t.KeyExists = 1;
    END;

SqlFiddle here

工作原理

  • 我们首先将尝试插入表中的所有行的列表投影到#temp表中,通过键列LEFT OUTER JOIN注意其中哪些ARE已经在基础表中{ {1}}检测重复标准。
  • 然后我们需要重复COL_D语句的实际工作,通过插入表中尚未存在的那些行(由于INSERT,我们已经删除了插入的责任引擎,需要自己做。)
  • 最后,我们使用新插入的&#39;更新匹配行中的所有非键列。数据。

显着点

  • 它在封面下工作,即在启用触发器时进入表格的任何插入都将受到触发(例如,应用程序ORM,其他存储过程等)。调用者通常会忘记INSTEAD OF触发器已就位。
  • 必须有一键来检测重复标准(自然或代理)。在这种情况下,我假设INSTEAD OF,但它可能是一个复合键。 (密钥但由于明显的原因不能COL_D,因为客户端不会插入标识)
  • 触发器适用于单行和多行INSERTS

NB

  • standard disclaimers with triggers适用,而IDENTITY触发更多 - 因为这会导致Sql Server的可观察行为发生意外变化,例如: - 即使是好的INSTEAD OF触发器也会导致为开发人员和没有意识到他们出现在桌面上的DBA而浪费了数小时的努力和挫折。
  • 这将影响表中的所有插入。不仅仅是你的。

答案 4 :(得分:0)

在sql server中没有DUPLICATE KEY UPDATE等价物,但是你可以使用merged和sql server匹配来完成这个,看看这里: multiple operations using merge