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

Cách lấy số tiếp theo trong một dãy số

Nếu bạn không duy trì một bảng truy cập, có hai lựa chọn. Trong một giao dịch, trước tiên hãy chọn MAX(seq_id) với một trong các gợi ý bảng sau:

  1. WITH(TABLOCKX, HOLDLOCK)
  2. WITH(ROWLOCK, XLOCK, HOLDLOCK)

TABLOCKX + HOLDLOCK là một chút quá mức cần thiết. Nó chặn các câu lệnh chọn thông thường, có thể được coi là nặng mặc dù giao dịch nhỏ.

A ROWLOCK, XLOCK, HOLDLOCK gợi ý về bảng có lẽ là một ý tưởng tốt hơn (nhưng:hãy đọc thêm phương án thay thế với bảng đếm trên). Ưu điểm là nó không chặn các câu lệnh select thông thường, tức là khi các câu lệnh select không xuất hiện trong SERIALIZABLE giao dịch, hoặc khi các câu lệnh chọn không cung cấp các gợi ý bảng giống nhau. Sử dụng ROWLOCK, XLOCK, HOLDLOCK sẽ vẫn chặn các câu lệnh chèn.

Tất nhiên, bạn cần đảm bảo rằng không có phần nào khác trong chương trình của bạn chọn MAX(seq_id) không có các gợi ý bảng này (hoặc bên ngoài SERIALIZABLE giao dịch) và sau đó sử dụng giá trị này để chèn các hàng.

Lưu ý rằng tùy thuộc vào số lượng hàng bị khóa theo cách này, có thể SQL Server sẽ chuyển khóa thành khóa bảng. Đọc thêm về báo cáo khóa tại đây .

Quy trình chèn bằng WITH(ROWLOCK, XLOCK, HOLDLOCK) sẽ trông như sau:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @max_seq INT=(SELECT MAX(seq) FROM dbo.table_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @max_seq IS NULL SET @max_seq=0;
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@max_seq+1,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Một giải pháp thay thế và có lẽ là một ý tưởng tốt hơn là có một bộ đếm bảng, và cung cấp các gợi ý bảng này trên bảng truy cập. Bảng này sẽ giống như sau:

CREATE TABLE dbo.counter_seq(model INT PRIMARY KEY, seq_id INT);

Sau đó, bạn sẽ thay đổi quy trình chèn như sau:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @new_seq INT=(SELECT seq FROM dbo.counter_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @new_seq IS NULL 
        BEGIN SET @new_seq=1; INSERT INTO dbo.counter_seq(model,seq)VALUES(@target_model,@new_seq); END
    ELSE
        BEGIN SET @new_seq+=1; UPDATE dbo.counter_seq SET [email protected]_seq WHERE [email protected]_model; END
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@new_seq,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Ưu điểm là sử dụng ít khóa hàng hơn (tức là một khóa cho mỗi kiểu máy trong dbo.counter_seq ) và khóa leo thang không thể khóa toàn bộ dbo.table_seq bảng do đó chặn các câu lệnh chọn.

Bạn có thể kiểm tra tất cả những điều này và tự xem các hiệu ứng bằng cách đặt WAITFOR DELAY '00:01:00' sau khi chọn trình tự từ counter_seq và tìm hiểu (các) bảng trong tab SSMS thứ hai.

PS1:Sử dụng ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) không phải là một cách tốt. Nếu hàng bị xóa / thêm hoặc ID thay đổi, trình tự sẽ thay đổi (hãy xem xét id hóa đơn không bao giờ thay đổi). Ngoài ra, về mặt hiệu suất, việc xác định số hàng của tất cả các hàng trước đó khi truy xuất một hàng là một ý tưởng tồi.

PS2:Tôi sẽ không bao giờ sử dụng các tài nguyên bên ngoài để cung cấp khóa, khi SQL Server đã cung cấp khóa thông qua các mức cách ly hoặc các gợi ý bảng chi tiết.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server chuyển đổi datetime thành int trong truy vấn

  2. Xóa tài khoản thư cơ sở dữ liệu khỏi hồ sơ (SSMS)

  3. Thông tin xác thực cho dịch vụ SQL Server Agent không hợp lệ

  4. Hành vi @@ rowcount không mong muốn bên trong UDF trong MS SQL 2019

  5. Cập nhật bản ghi trong bảng từ CTE