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

Nem*_*emo 24 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);
Run Code Online (Sandbox Code Playgroud)

有人可以帮忙吗?

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

Sql*_*Zim 20

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

我推荐这个:为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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

即使有创造性的使用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'
      );
Run Code Online (Sandbox Code Playgroud)

Spock提供的Merge答案应该做你想要的.

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


Spo*_*ock 9

试试这个...我已经添加了注释来尝试解释在SQL Merge语句中发生了什么.来源:MSDN:合并声明

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)
Run Code Online (Sandbox Code Playgroud)

希望有所帮助


Stu*_*tLC 5

您可以使用 模拟几乎相同的行为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;
Run Code Online (Sandbox Code Playgroud)

SqlFiddle在这里

这个怎么运作

  • 我们首先将尝试插入表中的所有行的列表投影到 #temp 表中,通过检测重复标准LEFT OUTER JOIN的键列上的a来注意哪些 ARE 已经在基础表中COL_D
  • 然后我们需要INSERT通过插入那些不在表中的行来重复语句的实际工作(因为INSTEAD OF,我们已经从引擎中删除了插入的责任,需要自己做这件事)。
  • 最后,我们用新插入的数据更新匹配行中的所有非键列。

要点

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

NB

  • 触发器标准免责声明适用,触发器更是如此INSTEAD OF- 因为这可能导致 Sql Server 的可观察行为发生令人惊讶的变化,例如这样 - 即使是精心设计的INSTEAD OF触发器也会导致不知道的开发人员和 DBA 花费数小时的精力和沮丧他们出现在你的桌子上。
  • 这将影响到表中的所有插入。不只是你的。