Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Làm cách nào để cập nhật bảng lớn với hàng triệu hàng trong SQL Server?

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

  2. 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:

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

    2. 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 đề.

  3. 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).

  4. 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ết TRY / 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:

  1. không có chỉ mục nào để hỗ trợ truy vấn hoặc
  2. 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à:

  1. Tạo #targetIds một cách rõ ràng bảng thay vì sử dụng SELECT INTO...
  2. Đố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.
  3. Đố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.
  4. Để chèn vào #targetIds , sử dụng INSERT INTO #targetIds (column_name(s)) SELECT 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).



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Chuyển đổi ‘datetime2’ thành ‘datetimeoffset’ trong SQL Server (Ví dụ T-SQL)

  2. Nhận ngày cuối cùng của tháng trong SQL

  3. Tại sao tôi không thể sử dụng bí danh trong cột đếm (*) và tham chiếu nó trong mệnh đề có?

  4. SQL Server 2016:Tạo một thủ tục được lưu trữ

  5. Làm thế nào để so sánh các phiên bản phần mềm sử dụng SQL Server?