-
Bạn không nên cập nhật 10k hàng trong một tập hợp trừ khi bạn chắc chắn rằng thao tác đang nhận được Khóa trang (do nhiều hàng trên mỗi trang là một phần của
UPDATE
hoạt động). Vấn đề là Khóa leo thang (từ khóa Hàng hoặc Trang đến Bảng) xảy ra ở 5000 khóa . Vì vậy, an toàn nhất là giữ nó ở mức dưới 5000, đề phòng hoạt động đang sử dụng Row Locks. -
Bạn không nên đang sử dụng SET ROWCOUNT để giới hạn số hàng sẽ được sửa đổi. Có hai vấn đề ở đây:
-
Nó đã không được dùng nữa kể từ khi SQL Server 2005 được phát hành (11 năm trước):
Việc sử dụng SET ROWCOUNT sẽ không ảnh hưởng đến các câu lệnh DELETE, INSERT và UPDATE trong bản phát hành SQL Server trong tương lai. Tránh sử dụng SET ROWCOUNT với các câu lệnh DELETE, INSERT và UPDATE trong công việc phát triển mới, đồng thời có kế hoạch sửa đổi các ứng dụng hiện đang sử dụng nó. Đối với một hành vi tương tự, hãy sử dụng cú pháp TOP
-
Nó có thể ảnh hưởng nhiều hơn đến tuyên bố mà bạn đang xử lý:
Việc đặt tùy chọn SET ROWCOUNT khiến hầu hết các câu lệnh Transact-SQL ngừng xử lý khi chúng bị ảnh hưởng bởi số hàng được chỉ định. Điều này bao gồm các trình kích hoạt. Tùy chọn ROWCOUNT không ảnh hưởng đến con trỏ động, nhưng nó giới hạn tập hợp các bộ phím và con trỏ không nhạy cảm. Tùy chọn này nên được sử dụng một cách thận trọng.
Thay vào đó, hãy sử dụng
TOP ()
mệnh đề. -
-
Không có mục đích giao dịch rõ ràng ở đây. Nó làm phức tạp mã và bạn không có cách nào xử lý
ROLLBACK
, thậm chí không cần thiết vì mỗi câu lệnh là giao dịch của chính nó (tức là tự động cam kết). -
Giả sử bạn tìm thấy lý do để giữ giao dịch rõ ràng, thì bạn không có
TRY
/CATCH
cấu trúc. Vui lòng xem câu trả lời của tôi trên DBA.StackExchange để biếtTRY
/CATCH
mẫu xử lý các giao dịch:Chúng tôi có bắt buộc phải xử lý Giao dịch bằng Mã C # cũng như trong thủ tục Cửa hàng không
Tôi nghi ngờ rằng WHERE
thực mệnh đề không được hiển thị trong mã ví dụ trong Câu hỏi, vì vậy chỉ cần dựa vào những gì đã được hiển thị, một tốt hơn mô hình sẽ là:
DECLARE @Rows INT,
@BatchSize INT; -- keep below 5000 to be safe
SET @BatchSize = 2000;
SET @Rows = @BatchSize; -- initialize just to enter the loop
BEGIN TRY
WHILE (@Rows = @BatchSize)
BEGIN
UPDATE TOP (@BatchSize) tab
SET tab.Value = 'abc1'
FROM TableName tab
WHERE tab.Parameter1 = 'abc'
AND tab.Parameter2 = 123
AND tab.Value <> 'abc1' COLLATE Latin1_General_100_BIN2;
-- Use a binary Collation (ending in _BIN2, not _BIN) to make sure
-- that you don't skip differences that compare the same due to
-- insensitivity of case, accent, etc, or linguistic equivalence.
SET @Rows = @@ROWCOUNT;
END;
END TRY
BEGIN CATCH
RAISERROR(stuff);
RETURN;
END CATCH;
Bằng cách thử nghiệm @Rows
chống lại @BatchSize
, bạn có thể tránh UPDATE
cuối cùng đó truy vấn (trong hầu hết các trường hợp) vì tập hợp cuối cùng thường có một số hàng nhỏ hơn @BatchSize
, trong trường hợp đó, chúng tôi biết rằng không còn phải xử lý nữa (đó là những gì bạn thấy trong kết quả hiển thị trong câu trả lời của bạn). Chỉ trong những trường hợp tập hợp hàng cuối cùng bằng @BatchSize
mã này có chạy UPDATE
cuối cùng không ảnh hưởng đến 0 hàng.
Tôi cũng đã thêm một điều kiện vào WHERE
để ngăn các hàng đã được cập nhật không được cập nhật lại.
LƯU Ý VỀ HIỆU SUẤT
Tôi đã nhấn mạnh "tốt hơn" ở trên (như trong, "đây là một tốt hơn model ") bởi vì điều này có một số cải tiến so với mã gốc của O.P. và hoạt động tốt trong nhiều trường hợp, nhưng không hoàn hảo cho mọi trường hợp. Đối với các bảng có ít nhất một kích thước nhất định (thay đổi do một số yếu tố nên tôi có thể ' không cụ thể hơn), hiệu suất sẽ giảm xuống vì có ít hàng cần sửa hơn nếu:
- không có chỉ mục nào để hỗ trợ truy vấn hoặc
- có một chỉ mục, nhưng có ít nhất một cột trong
WHERE
mệnh đề là một kiểu dữ liệu chuỗi không sử dụng đối chiếu nhị phân, do đóCOLLATE
mệnh đề được thêm vào truy vấn ở đây để buộc đối chiếu nhị phân và làm như vậy sẽ làm mất hiệu lực của chỉ mục (đối với truy vấn cụ thể này).
Đây là tình huống mà @mikesigs gặp phải, do đó yêu cầu một cách tiếp cận khác. Phương thức đã cập nhật sao chép ID cho tất cả các hàng được cập nhật vào một bảng tạm thời, sau đó sử dụng bảng tạm thời đó để INNER JOIN
vào bảng đang được cập nhật trên (các) cột khóa chỉ mục được nhóm. (Điều quan trọng là phải nắm bắt và tham gia vào chỉ mục được nhóm , cho dù đó có phải là cột khóa chính hay không!).
Vui lòng xem câu trả lời của @mikesigs bên dưới để biết chi tiết. Cách tiếp cận được thể hiện trong câu trả lời đó là một mô hình rất hiệu quả mà bản thân tôi đã sử dụng nhiều lần. Những thay đổi duy nhất tôi sẽ thực hiện là:
- Tạo
#targetIds
một cách rõ ràng bảng thay vì sử dụngSELECT INTO...
- Đối với
#targetIds
bảng, khai báo một khóa chính được phân nhóm trên (các) cột. - Đối với
#batchIds
bảng, khai báo một khóa chính được phân nhóm trên (các) cột. - Để chèn vào
#targetIds
, sử dụngINSERT INTO #targetIds (column_name(s)) SELECT
và loại bỏORDER BY
vì nó không cần thiết.
Vì vậy, nếu bạn không có chỉ mục có thể được sử dụng cho thao tác này và tạm thời không thể tạo chỉ mục thực sự hoạt động (chỉ mục được lọc có thể hoạt động, tùy thuộc vào WHERE
của bạn mệnh đề cho UPDATE
truy vấn), sau đó thử cách tiếp cận được hiển thị trong câu trả lời @mikesigs (và nếu bạn sử dụng giải pháp đó, vui lòng bỏ phiếu).