2011-07-06 45 views
12

Tôi đang cố gắng chèn nhiều bản ghi bằng cách sử dụng câu lệnh MERGE của T-SQL, nhưng truy vấn của tôi không INSERT khi có bản ghi trùng lặp trong bảng nguồn. Sự thất bại là do:Cách tránh chèn bản ghi trùng lặp khi sử dụng câu lệnh T-SQL Merge

  1. Bảng mục tiêu có một Primary Key dựa trên hai cột
  2. các bảng nguồn có thể chứa dữ liệu trùng vi phạm chế Primary Key bảng mục tiêu ("Vi phạm các hạn chế PRIMARY KEY" là ném)

Tôi đang tìm cách thay đổi câu lệnh MERGE để bỏ qua các bản ghi trùng lặp trong bảng nguồn và/hoặc sẽ thử/nắm bắt câu lệnh INSERT để bắt ngoại lệ có thể xảy ra (nghĩa là tất cả các câu lệnh INSERT khác sẽ chạy bất kể vài quả trứng xấu có thể xảy ra) - hoặc, có thể, có một cách tốt hơn o đi về vấn đề này?

Dưới đây là ví dụ truy vấn về những gì tôi đang cố giải thích. Ví dụ dưới đây sẽ thêm 100k hồ sơ vào một bảng tạm thời và sau đó sẽ cố gắng để chèn những bản ghi trong bảng mục tiêu -

EDIT Trong bài ban đầu của tôi tôi chỉ bao gồm hai trường trong bảng ví dụ mà nhường chỗ cho SO bạn bè để đưa ra giải pháp DISTINCT để tránh trùng lặp trong tuyên bố MERGE. Tôi cần phải đề cập rằng trong vấn đề thực tế của tôi, các bảng có 15 trường và 15 trường, hai trong số các trường là một khóa CHÍNH XÁC ĐÁP. Vì vậy, từ khóa DISTINCT không hoạt động vì tôi cần phải SELECT tất cả 15 trường và bỏ qua các bản sao dựa trên hai trường.

Tôi đã cập nhật truy vấn bên dưới để bao gồm một trường nữa, col4. Tôi cần phải bao gồm col4 trong MERGE, nhưng tôi chỉ cần chắc chắn rằng chỉ col2 và col3 là duy nhất.

-- 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

Bạn phải quyết định hàng nào bạn nên chọn col4 từ khi có các bản sao cho col2 và col3 trong #tmp. Ví dụ, bạn có thể sử dụng 'nhóm bằng col2, col3' và' min (col4) làm col4'. –

Trả lời

15

Giải Quyết đặc điểm kỹ thuật mới của bạn. Chỉ chèn giá trị cao nhất của col4: Lần này tôi đã sử dụng một nhóm để ngăn các hàng trùng lặp.

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

Tôi đã phạm sai lầm khi chỉ sử dụng hai trường trong ví dụ truy vấn của mình. Thực tế là, bảng mục tiêu của tôi có nhiều hơn hai trường. Tuy nhiên, nó chỉ có hai trường tạo nên cụm PK. Vì vậy, giải pháp DISTINCT mà bạn đề xuất sẽ không đủ. Tôi đã cập nhật bài đăng gốc của mình để phản ánh các trường bổ sung. Bất kể, nhờ câu trả lời og của bạn (+1 cho trả lời câu hỏi như là). – Jed

+1

Ok, tôi đã thử một lần thử mới. Tôi hy vọng tôi hiểu nó đúng. –

7

Với nguồn có bản sao và bạn không sử dụng MERGE đầy đủ, tôi sẽ sử dụng 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) 

Lý do MERGE không thành công là nó không được kiểm tra từng hàng. Tất cả không phù hợp được tìm thấy, sau đó nó cố gắng INSERT tất cả những điều này. Nó không kiểm tra các hàng trong cùng một lô đã khớp.

này nhắc tôi một chút của "Halloween problem" nơi thay đổi dữ liệu ban đầu của một hoạt động nguyên tử ảnh hưởng đến dữ liệu thay đổi sau: nó không phải là chính xác

+0

Tôi chưa thử nghiệm tập lệnh của riêng mình, bạn có nói rằng nó không thành công? –

+0

@ t-clausen.dk: Bạn có một DISTINCT quá nên nên OK. Với giới hạn của MERGE tại sao không chỉ sử dụng INSERT I reckon ... – gbn

+0

Vì tiêu đề của câu hỏi –

2

Thay vì GROUP BY bạn có thể sử dụng chức năng phân tích, cho phép bạn chọn một bản ghi cụ thể trong tập hợp các bản ghi trùng lặp để hợp nhất.

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) 
Các vấn đề liên quan