2013-10-03 15 views
5

Tôi có yêu cầu pháp lý trong bộ sưu tập hóa đơn của ứng dụng (sử dụng SQL Server) của chúng tôi không thể có khoảng trống trong việc đánh số chúng. Vì vậy, nếu đây là số hóa đơn, điều này sẽ không được phép: [1, 2, 3, 4, 8, 10] bởi vì nó không phải tuần tự. Để kết thúc, chúng tôi có một cột InvoiceNumber trên bảng Invoices của chúng tôi. Thêm vào đó, chúng tôi có một bảng InvoiceNumbers chứa số hóa đơn hiện tại cho mỗi tổ chức (vì mỗi tổ chức cần phải có trình tự riêng của mình). Một thủ tục được lưu trữ sau đó chịu trách nhiệm điền vào số InvoiceNumber trên Invoices nguyên tử; nó sẽ tăng bộ đếm hiện tại lên 1 trong bảng InvoiceNumbers và điền vào giá trị mới đó vào bảng Invoices hoặc cuộn lại giao dịch trong trường hợp có lỗi. Điều này hoạt động tốt.Tạo số hóa đơn tuần tự trong SQL Server mà không có điều kiện chủng tộc

Bây giờ, yêu cầu mới đã được thêm vào: các đơn đặt hàng nhất định phải chia sẻ cùng một hóa đơn và do đó cùng một số hóa đơn, trong khi mỗi đơn hàng trước đó được lập hóa đơn riêng. Để kết thúc này, chúng tôi tạo một hóa đơn vào đầu ngày và kết hợp nó với FinancialPeriod hiện tại (ngày làm việc, về cơ bản) sẽ là hóa đơn được sử dụng cho mọi đơn đặt hàng. Tuy nhiên, có thể tổ chức không tạo bất kỳ đơn đặt hàng nào thuộc loại yêu cầu lập hóa đơn được chia sẻ và do đó không có gì để lập hóa đơn trong một ngày mà "lãng phí" hóa đơn ban đầu được tạo ra (vì ngày hôm sau, một hóa đơn mới được tạo) và tạo lỗ hổng.

Bây giờ, giải pháp đơn giản nhất đối với tôi là phải điền nhẹ vào số InvoiceNumber trên hóa đơn được chia sẻ được tạo vào đầu ngày. Nếu một đơn đặt hàng được tạo vào ngày đó và InvoiceNumber vẫn là NULL, thì hãy tạo số. Điều này sẽ đảm bảo một InvoiceNumber không bao giờ đi không sử dụng (nó không quan trọng là một bản ghi Invoice đi không sử dụng, nó không có ý nghĩa thực sự).

Để kết thúc, tôi đã tạo quy trình được lưu trữ bên dưới, cho số Invoice hiện tại, điền vào số InvoiceNumber nhưng chỉ khi vẫn còn NULL. Tôi chỉ không chắc chắn về cách SQL Server khóa và nếu có tiềm năng cho một điều kiện chủng tộc nơi hai giao dịch cơ sở dữ liệu quyết định rằng InvoiceNumber vẫn là NULL và cả hai sẽ tăng bộ đếm và lãng phí một số, tạo khoảng trống.

Về cơ bản, câu hỏi dài có này này tóm tắt: có thể hai giao dịch cơ sở dữ liệu đồng thời quyết định nhập khối if(@currentNumber is null) cho cùng một số @invoiceID tại đây không?

Các khóa phần bạn thấy tôi đã nhận được từ đây, nhưng tôi không chắc chắn nó áp dụng cho trường hợp của tôi:

Pessimistic lock in T-SQL

CREATE PROCEDURE [dbo].[CreateInvoiceNumber] 
    @invoiceID int, 
    @appID int 
AS 
BEGIN 

    SET NOCOUNT ON; 

    if not exists (select 1 from InvoiceNumbers where ApplicationID = @appID) insert into InvoiceNumbers values (@appID, 1) 

    declare @currentNumber int = null; 

    select @currentNumber = convert(int, i.InvoiceNumber) 
    from Invoices i 
    with (HOLDLOCK, ROWLOCK) 
    where i.ID = @invoiceID 

    if(@currentNumber is null) 
    begin 
     update InvoiceNumbers set @currentNumber = Value = Value + 1 
      where ApplicationID = @appID 

     update Invoices set InvoiceNumber = @currentNumber where ID = @invoiceID   
    end 

    select convert(nvarchar, @currentNumber) 
END 

EDIT

Như đã đề cập trong bình luận của tôi, những hoạt động viết và viết này là một phần của một giao dịch cơ sở dữ liệu được khởi tạo từ logic ứng dụng C#. Chỉ cần BeginTransaction thông thường trên SqlConnection với các tùy chọn mặc định, tất nhiên là được quay lại trong trường hợp có bất kỳ ngoại lệ nào.

+0

Xem câu hỏi này và đó là câu trả lời cho giải pháp đã được thử và thực sự cho sự cố của bạn. http://dba.stackexchange.com/questions/36603/handling-concurrent-access-to-a-key-table-without-deadlocks-in-sql-server –

+0

@MaxVernon - Đúng nếu tôi sai, nhưng dường như nhắm vào việc làm những gì tôi đang làm nhưng tránh và phục hồi từ những người chết, đó không phải là ưu tiên đối với tôi. Dù bằng cách nào, thật khó để chưng cất những gì tôi cần từ câu trả lời đó, vì nó khá phức tạp hơn những gì tôi có cho đến nay. – JulianR

+0

Đó là lý do tại sao tôi không đăng câu trả lời đó. –

Trả lời

1

Đảm bảo rằng mức cô lập cơ sở dữ liệu đã được đặt thành READ COMMITTED.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

Đây là mức cách ly mặc định. Nó đảm bảo rằng tất cả các giao dịch phải được cam kết trước khi hàng được đọc, do đó không có đọc bẩn xảy ra.Cũng cần lưu ý quan trọng, khi cập nhật bảng InvoiceNumbers, đảm bảo rằng nó đang trong một giao dịch, bạn muốn áp dụng nguyên tắc ACID tại đây và mọi thứ là nguyên tử (cam kết toàn bộ đơn vị hoặc giao dịch quay lại).

+0

Cảm ơn bạn. Tôi cần phải có đề cập rằng mã ứng dụng C# kết thúc tốt đẹp hoạt động này và bất kỳ viết khác xảy ra trong một DbTransaction. Vì vậy, nếu đó là trường hợp, thủ tục này được lưu trữ nên được chủng tộc-điều kiện miễn phí? – JulianR

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