2013-03-13 49 views
5

Tôi đang chạy một thủ tục lưu trữ mà chọn giá trị bảng temp của tôi và chèn chúng vào cơ sở dữ liệu như vậy:INSERT INTO .. ​​CHỌN .. độc đáo hạn chế vi phạm

INSERT INTO emails (EmailAddress) (
    SELECT 
     DISTINCT eit.EmailAddress 
    FROM #EmailInfoTemp eit 
     LEFT JOIN emails ea 
     ON eit.EmailAddress = ea.EmailAddress 
    WHERE ea.EmailAddressID IS NULL ) 

Mở trường hợp hiếm hoi (~ một lần mỗi vài số giờ trên một máy chủ xử lý hàng nghìn yêu cầu một phút), sau đó tôi nhận được một lỗi ràng buộc duy nhất "Vi phạm ràng buộc KEY UNIQUE" .. trên một chỉ mục trên cột EmailAddress.

Tôi có thể xác nhận rằng tôi không chuyển giá trị trùng lặp. Ngay cả khi tôi đã, nó sẽ bị bắt bởi DISTINCT.

server -SQL 2008 proc -Stored + không sử dụng các giao dịch + JDBC CallableStatement

Nó có thể xảy ra rằng giữa SELECT và INSERT sau đó, đã có một cuộc gọi đến cùng proc lưu trữ/khác nhau mà hoàn thành một INSERT với dữ liệu tương tự? Nếu vậy, cách tốt nhất để ngăn chặn điều đó là gì?

Một số ý tưởng: Chúng tôi có nhiều trường hợp trùng lặp "khách hàng" giao tiếp với SQL Server này một lần trong sản xuất, vì vậy phản ứng đầu tiên của tôi là vấn đề tương tranh, nhưng dường như tôi không thể tự sao chép nó. Đó là dự đoán tốt nhất tôi có, nhưng nó không còn ở đâu cả. Điều này không xảy ra trên môi trường dàn dựng của chúng tôi khi tải không đáng kể so với môi trường sản xuất. Đó là lý do chính tôi bắt đầu xem xét các vấn đề tương tranh.

+1

Giá trị NULL trong EmailAddress ở cả hai bên có thể vi phạm ràng buộc khóa duy nhất. Nếu cột này là nullable bạn có thể viết lại chèn như 'không tồn tại'. –

+0

Điểm tốt. Tôi kiểm tra với null trong mã, nhưng tôi cho rằng tôi có thể kiểm tra lại nó trong sql. –

Trả lời

5

Lỗi có thể do hai phiên thực thi chèn cùng một lúc.

Bạn có thể làm cho mã SQL của mình an toàn hơn bằng cách sử dụng MERGE. Như bình luận của Aaron Bertrand nói (cảm ơn!), you have to include a with (holdlock) hint to make merge really safe.

; merge emails e with (holdlock) 
using #EmailInfoTemp eit 
on  e.EmailAddress = eit.EmailAddress 
when not matched then insert 
     (EmailAddress) values (eit.EmailAddress) 

Tuyên bố merge sẽ mất khóa thích hợp để đảm bảo rằng không có phiên nào khác có thể lẻn vào giữa nó "không phù hợp" kiểm tra và "chèn".

Nếu bạn không thể sử dụng merge, bạn có thể giải quyết vấn đề phía máy khách. Hãy chắc chắn rằng không có hai chèn đang chạy cùng một lúc. Điều này thường dễ thực hiện với một cấu trúc đồng bộ hóa mutex.

+1

['MERGE' không nhất thiết phải an toàn hơn trừ khi bạn thêm gợi ý' HOLDLOCK'] (http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx). –

+0

@AaronBertrand: Cảm ơn. Trang MSDN cho MERGE nói "Chỉ định READPAST với WHEN NOT MATCHED [THE TARGET] THEN INSERT có thể dẫn đến các hoạt động INSERT vi phạm các ràng buộc UNIQUE." Vì vậy, tôi giả định rằng nó đã được an toàn, trừ khi bạn chỉ định 'readpast'. – Andomar

+1

vâng Microsoft thường không có khả năng ghi lại tất cả các trường hợp nó có thể bị hỏng. Tài liệu cũng không nói về [bất kỳ lỗi nào chưa được giải quyết này (cuộn xuống dưới)] (http://www.sqlperformance.com/2013/02/t-sql-queries/another- hợp nhất-lỗi). :-) –

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