2014-11-22 136 views
15

我想在Sql Server(2012)中找到以下MySql查詢的等效項?等價的MySQL在SQL Server中的重複密鑰更新

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查詢具有類似的功能,但我發現它的語法非常不同。

+1

參見[這個問題](HTTP:// stackoverflow.com/questions/108403/solutions-for-insert-or-update-on-sql-server)尋求幫助。 – Phylogenesis 2014-11-22 10:32:30

+1

這也可以是有用的:http://stackoverflow.com/questions/1197733/does-sql-server-offer-anything-like-mysqls-on-duplicate-key-update – 2014-12-18 09:52:03

+0

你是否需要這種行爲的所有插入,或只是特定的? – StuartLC 2014-12-25 09:48:06

回答

6

試試這個... 我已經添加了註釋來嘗試解釋SQL Merge語句中發生了什麼。 來源:MSDN : Merge Statement

合併聲明不同於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

存儲過程將保存一天。

這裏我假設COL_A和COL_B是唯一的列,並且是INT 的類型!沒有sql-server實例ATM,所以不能保證語法的正確性。 更新!下面是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 
+1

您可以通過嘗試更新記錄來改進此解決方案,然後檢查@@ ROWCOUNT是否等於零,如果等於,那麼您可以插入記錄。通過這種優化,您可以保存搜索(索引搜索或索引掃描)。 – Pred 2014-12-19 07:55:00

+0

同意,這將是更優雅的解決方案 – Artjoman 2014-12-19 08:47:26

15

你基本上是尋找一個插入或更新模式有時被稱爲的Upsert 。

我建議這樣的:Insert or Update pattern for Sql Server - 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 

薩姆藏紅花的第二個解決方案(適用於該架構):

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,你仍然可以不必使用插入/更新塊或MERGE語句卡住。

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

您可以使用INSTEAD OF TRIGGER模擬近identitical行爲:

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表中,注意哪些已經在底層表通過檢測重複標準的關鍵列COL_D上的LEFT OUTER JOIN
  • 然後,我們需要重複INSERT語句的實際工作,方法是插入那些不在表格中的行(由於INSTEAD OF,我們已經刪除了從引擎插入的責任,並且需要自行完成) 。
  • 最後,我們用新插入的數據更新匹配行中的所有非鍵列。

突出點

  • 它工作在幕後,即任何插入到表而被使能的觸發器將受到觸發(例如應用ORM,其他存儲過程等)。來電者通常是UNAWARE,其觸發器已到位。
  • 必須有一個關鍵的排序來檢測重複的標準(自然或代理)。在這種情況下,我假定了COL_D,但它可能是一個複合鍵。 (關鍵,但不能IDENTITY原因很明顯,因爲客戶不會被插入標識)
  • 觸發適用於單個和多個行插入

NB

  • standard disclaimers with triggers適用,而更多的是INSTEAD OF觸發器 - 因爲這可能會導致Sql Server的可觀察行爲發生令人驚訝的變化,比如這種情況 - 即使打算好的INSTEAD OF觸發器也會導致數小時的開發人員和DBA的浪費精力和沮喪誰不知道他們在你的桌子上的存在。
  • 這將影響到表中的所有插入。不只是你的。
+0

我很驚訝,這個選項沒有更多upvotes。這是一種在MSSQL中構建Upsert的創造性方法。我唯一關心的是查詢的效率,因爲行必須複製到臨時表中。如果插入影響了數十萬或數百萬行,那麼這種觸發器的有效性是多少,需要先複製每一個受影響的行? – 2016-06-29 18:24:10

0

有相當於在SQL Server中沒有DUPLICATE KEY UPDATE,但你可以使用合併和當SQL Server的匹配來完成這件事,看看這裏: multiple operations using merge