2013-02-11 51 views
43

Tôi đã có một bảng với dữ liệu có tên energydataSQL lệnh MERGE để cập nhật dữ liệu

nó chỉ có ba cột

(webmeterID, DateTime, kWh) 

Tôi có một bộ mới của dữ liệu được cập nhật trong một bảng temp_energydata.

DateTimewebmeterID giữ nguyên. Nhưng giá trị kWh cần cập nhật từ bảng temp_energydata.

Làm cách nào để viết T-SQL cho cách này đúng?

+0

Có bản ghi nào trong 'temp_energydata' không có trong 'energydata' không? –

Trả lời

96

Giả sử bạn muốn có một thực tế SQL Server MERGE tuyên bố:

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target 
USING dbo.temp_energydata AS source 
    ON target.webmeterID = source.webmeterID 
    AND target.DateTime = source.DateTime 
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (webmeterID, DateTime, kWh) 
    VALUES (source.webmeterID, source.DateTime, source.kWh); 

Nếu bạn cũng muốn xóa các bản ghi trong mục tiêu mà không phải là trong nguồn:

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target 
USING dbo.temp_energydata AS source 
    ON target.webmeterID = source.webmeterID 
    AND target.DateTime = source.DateTime 
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (webmeterID, DateTime, kWh) 
    VALUES (source.webmeterID, source.DateTime, source.kWh) 
WHEN NOT MATCHED BY SOURCE THEN 
    DELETE; 

Bởi vì điều này đã trở thành một chút mor e phổ biến, tôi cảm thấy như tôi nên mở rộng câu trả lời này một chút với một số thông báo cần lưu ý.

Trước tiên, có một số blog báo cáo concurrency issues with the MERGE statement. Điều này phần lớn có thể được làm việc xung quanh bằng cách xác định gợi ý HOLDLOCK hoặc SERIALIZABLE khóa:

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target 
[...] 

Bạn cũng có thể thực hiện điều tương tự với mức cô lập giao dịch hạn chế hơn.

several other known issues với MERGE. Từ những gì tôi có thể nói, hầu hết trong số họ không phải là vấn đề phổ biến hoặc có thể được làm việc xung quanh với các gợi ý khóa như trên, nhưng tôi đã không thử nghiệm chúng.

Vì nó là, mặc dù tôi chưa bao giờ có bất kỳ vấn đề nào với tuyên bố MERGE, tôi luôn sử dụng gợi ý WITH (HOLDLOCK) ngay bây giờ và tôi chỉ muốn sử dụng câu lệnh trong trường hợp đơn giản nhất.

+2

Điều khoản 'KHÔNG ĐƯỢC CHẤP NHẬN THEO SOURCE' có thể cần được sử dụng thận trọng trong trường hợp này. Nếu 'temp_energydata' chứa các cập nhật chỉ cho một tập con của các thành viên trong' energydata', thì MERGE thứ hai của bạn sẽ xóa dữ liệu của ** tất cả ** thành viên không tìm thấy trong tập hợp tạm thời. –

+1

@AndriyM Đó là lý do tại sao tôi nói "Nếu bạn cũng muốn xóa các bản ghi trong mục tiêu không có trong nguồn". Tôi không chắc làm thế nào điều này sẽ gây nhầm lẫn? –

+0

Vâng, có lẽ không khó hiểu nhưng, với một người thiếu kinh nghiệm, nó * có thể * không hoàn toàn rõ ràng rằng, khi họ muốn sử dụng bộ temp để cập nhật một tập con của các hàng (đặc biệt, một tập con của các thành viên) trong bảng chính , các hàng đã xóa cũng sẽ bao gồm các thành viên không được cho là được cập nhật. Tôi không nhấn mạnh (rằng nó có thể không được rõ ràng), mặc dù, như tôi có thể cũng chỉ là overcautious có, vì vậy xin vui lòng bỏ qua bình luận của tôi nếu bạn nghĩ như vậy. –

3

Nếu bạn chỉ cần cập nhật hồ sơ của bạn trong energydata dựa trên dữ liệu trong temp_energydata, giả định rằng temp_enerydata không chứa bất kỳ kỷ lục mới, sau đó thử này:

UPDATE e SET e.kWh = t.kWh 
    FROM energydata e INNER JOIN 
     temp_energydata t ON e.webmeterID = t.webmeterID AND 
          e.DateTime = t.DateTime 

đây đang làm việc sqlfiddle

Nhưng nếu temp_energydata chứa các bản ghi mới và bạn cần phải chèn nó vào energydata tốt hơn với một tuyên bố thì bạn chắc chắn nên đi với câu trả lời mà Bacon Bits đã đưa ra.

0
UPDATE ed 
SET ed.kWh = ted.kWh 
FROM energydata ed 
INNER JOIN temp_energydata ted ON ted.webmeterID = ed.webmeterID 
+0

Điều đó rất có thể sẽ ghi đè lên chỉ số đồng hồ trong 'energydata' cho những ngày khác với những ngày trong' temp_energydata', có thể là do kết quả đáng ngạc nhiên và không mong muốn. – peterm

0
Update energydata set energydata.kWh = temp.kWh 
where energydata.webmeterID = (select webmeterID from temp_energydata as temp) 
+0

Điều đó rất có thể sẽ ghi đè lên chỉ số đồng hồ trong 'energydata' cho những ngày khác với những ngày trong' temp_energydata', có thể là do kết quả đáng ngạc nhiên và không mong muốn. – peterm

-6

cách chính xác là:

UPDATE test1 
INNER JOIN test2 ON (test1.id = test2.id) 
SET test1.data = test2.data 
+3

Không nếu có hồ sơ MỚI trong 'temp_energydata'. Chắc chắn, bạn có thể thêm 'INSERT INTO ... SELECT * FROM ... cũ LEFT JOIN new WHERE old.foo IS NULL' (trước hoặc sau UPDATE) nhưng nó là hai câu lệnh và nếu có đủ dữ liệu thời gian thực hiện có thể đủ dài để gây ra các vấn đề trừ khi bạn LOCK bảng và nếu bạn làm điều đó bạn có khả năng làm người dùng giận dữ (không đủ không gian ở đây để đi vào tất cả các kịch bản). Tất cả những gì đã nói, tôi CẬP NHẬT CẬP NHẬT sau đó INSERT (hoặc ngược lại) bản thân mình nhưng nó không trả lời câu hỏi của OP. –

11

Tôi thường sử dụng Bacon Bits câu trả lời tuyệt vời như tôi chỉ không thể nhớ cú pháp.

Nhưng tôi thường thêm CTE làm phần bổ sung để làm cho phần DELETE hữu ích hơn vì rất thường bạn sẽ chỉ áp dụng hợp nhất vào một phần của bảng mục tiêu.

WITH target as (
    SELECT * FROM dbo.energydate WHERE DateTime > GETDATE() 
) 
MERGE INTO target WITH (HOLDLOCK) 
USING dbo.temp_energydata AS source 
    ON target.webmeterID = source.webmeterID 
    AND target.DateTime = source.DateTime 
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (webmeterID, DateTime, kWh) 
    VALUES (source.webmeterID, source.DateTime, source.kWh) 
WHEN NOT MATCHED BY SOURCE THEN 
    DELETE 
+0

bạn cũng có thể tăng cường mệnh đề USING của bạn lên một câu lệnh SELECT đầy đủ. Điều này hoạt động tốt nếu truy vấn rất đơn giản, nhưng tôi đã thấy các kế hoạch thực hiện rất xấu nếu truy vấn có nhiều hơn 1-2 bảng. Trong trường hợp này, tôi sẽ sử dụng bảng #temp hoặc CTE, theo ví dụ của bạn –

Các vấn đề liên quan