Như Paul viết: Không, nó không an toàn , mà tôi muốn thêm bằng chứng thực nghiệm:Tạo bảng Table_1
với một trường ID
và một bản ghi có giá trị 0
. Sau đó, thực thi mã sau đồng thời trong hai cửa sổ truy vấn của Management Studio :
declare @counter int
set @counter = 0
while @counter < 1000
begin
set @counter = @counter + 1
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
end
Sau đó thực thi
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
Trên SQL Server 2008 của tôi, một ID (662
) đã được tạo hai lần. Do đó, mức cách ly mặc định được áp dụng cho các câu lệnh đơn lẻ là không đủ.
CHỈNH SỬA:Rõ ràng, gói INSERT
với BEGIN TRANSACTION
và COMMIT
sẽ không sửa được, vì mức cô lập mặc định cho các giao dịch vẫn là READ COMMITTED
, mà là không đủ. Lưu ý rằng đặt mức cô lập giao dịch thành REPEATABLE READ
là cũng không đủ. Cách duy nhất để làm cho mã trên an toàn là thêm
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
ở trên cùng. Tuy nhiên, điều này đã gây ra bế tắc thỉnh thoảng trong các thử nghiệm của tôi.
CHỈNH SỬA:Giải pháp duy nhất tôi tìm thấy là an toàn và không tạo ra deadlock (ít nhất là trong các thử nghiệm của tôi) là để khóa bảng một cách rõ ràng (mức cách ly giao dịch mặc định là đủ ở đây). Hãy cẩn thận mặc dù; giải pháp này có thể giết chết hiệu suất:
...loop stuff...
BEGIN TRANSACTION
SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
COMMIT
...loop end...