2011-07-06 58 views
12

我試圖使用T-SQL的MERGE語句插入許多記錄,但是當源表中有重複記錄時,我的查詢無法插入。如何避免在使用T-SQL合併語句時插入重複記錄

  1. 目標表有一個主鍵基於兩列
  2. 源表可能包含違反目標表的主鍵約束(「PRIMARY KEY約束衝突」重複記錄的是:失敗是造成拋出)

我正在尋找一種方法來更改我的MERGE語句,以便它可以忽略源表中的重複記錄和/或將嘗試/捕獲INSERT語句來捕獲可能發生的異常(即所有其他INSERT語句將運行,而不管可能發生的少量壞蛋) - 或者,也許有更好的方法o解決這個問題?

下面是我試圖解釋的一個查詢示例。下面的例子將增加10萬點的記錄到一個臨時表,然後將嘗試插入到目標表中的記錄 -

編輯 在我原來的職位,我只包括在讓路的例子表到兩個領域SO朋友提供DISTINCT解決方案以避免MERGE語句中出現重複。我應該提到,在我現實世界的問題中,表格有15個字段,其中有15個字段是兩個字段是一個CLUSTERED PRIMARY KEY。所以DISTINCT關鍵字不起作用,因爲我需要選擇所有15個字段並忽略基於兩個字段的重複項。

我已更新下面的查詢以包含一個字段col4。我需要將col4包含在MERGE中,但我只需確保只有col2和col3是唯一的。

-- Create the source table 
CREATE TABLE #tmp (
col2 datetime NOT NULL, 
col3 int NOT NULL, 
col4 int 
) 
GO 

-- Add a bunch of test data to the source table 
-- For testing purposes, allow duplicate records to be added to this table 
DECLARE @loopCount int = 100000 
DECLARE @loopCounter int = 0 
DECLARE @randDateOffset int 
DECLARE @col2 datetime 
DECLARE @col3 int 
DECLARE @col4 int 

WHILE (@loopCounter) < @loopCount 
BEGIN 
    SET @randDateOffset = RAND() * 100000 
    SET @col2 = DATEADD(MI,@randDateOffset,GETDATE()) 
    SET @col3 = RAND() * 1000 
    SET @col4 = RAND() * 10 
    INSERT INTO #tmp 
    (col2,col3,col4) 
    VALUES 
    (@col2,@col3,@col4); 

    SET @loopCounter = @loopCounter + 1 
END 

-- Insert the source data into the target table 
-- How do we make sure we don't attempt to INSERT a duplicate record? Or how can we 
-- catch exceptions? Or? 
MERGE INTO dbo.tbl1 AS tbl 
    USING (SELECT * FROM #tmp) AS src 
    ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3) 
    WHEN NOT MATCHED THEN 
     INSERT (col2,col3,col4) 
     VALUES (src.col2,src.col3,src.col4); 
GO 
+0

您必須決定在#tmp中有col2和col3重複的時候應該選擇哪一行。例如,您可以使用'group by col2,col3'和'min(col4)col4'。 –

回答

15

解決您的新規範。只插入最高值的col4:這次我使用了group by來防止重複的行。

MERGE INTO dbo.tbl1 AS tbl 
USING (SELECT col2,col3, max(col4) col4 FROM #tmp group by col2,col3) AS src 
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3) 
WHEN NOT MATCHED THEN 
    INSERT (col2,col3,col4) 
    VALUES (src.col2,src.col3,src.col4); 
+0

我犯了我的查詢示例中只使用兩個字段的錯誤。事實是,我的目標表有兩個以上的字段。但是,組成PK羣集的只有兩個字段。所以你提出的DISTINCT解決方案是不夠的。我更新了原始文章以反映其他字段。無論如何,感謝您的答覆(+1回答問題是)。 – Jed

+1

好吧,我給了它一個新的嘗試。我希望我理解正確。 –

7

鑑於源代碼有重複,並且您沒有完全使用MERGE,我會使用INSERT。

INSERT dbo.tbl1 (col2,col3) 
SELECT DISTINCT col2,col3 
FROM #tmp src 
WHERE NOT EXISTS (
     SELECT * 
     FROM dbo.tbl1 tbl 
     WHERE tbl.col2 = src.col2 AND tbl.col3 = src.col3) 

MERGE失敗的原因是它沒有被逐行檢查。所有不匹配都會被找到,然後它會嘗試插入所有這些。它不檢查同一批次中已經匹配的行。

這讓我想起了一下"Halloween problem"的其中一個原子操作的早期數據的變化會影響以後數據的變化:這是不正確的

+0

我還沒有測試過我自己的腳本,你說它失敗了嗎? –

+0

@ t-clausen.dk:你也有DISTINCT,所以應該沒問題。鑑於MERGE的限制,爲什麼不使用INSERT,我認爲... – gbn

+0

由於問題的標題爲 –

2

取代GROUP BY,您可以使用分析函數,從而允許您選擇要合併的重複記錄集中的特定記錄。

MERGE INTO dbo.tbl1 AS tbl 
USING (
    SELECT * 
    FROM (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY col2, col3 ORDER BY ModifiedDate DESC) AS Rn 
     FROM #tmp 
    ) t 
    WHERE Rn = 1 --choose the most recently modified record 
) AS src 
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)