2012-01-21 24 views
11

Tôi cần tăng số nguyên trong cột SQL Server 2008.Tránh các sự cố đồng thời với MAX + 1 số nguyên trong SQL Server 2008 ... tạo giá trị IDENTITY riêng

Có vẻ như tôi nên sử dụng cột IDENTITY, nhưng tôi cần tăng bộ đếm riêng cho từng khách hàng của mình. Hãy nghĩ đến trang web thương mại điện tử nơi mỗi khách hàng có số đơn đặt hàng gia tăng của riêng họ, bắt đầu bằng 1. Giá trị phải là duy nhất (trên mỗi khách hàng).

Ví dụ,

Customer1 (Order #s 1,2,3,4,5...) 
Customer2 (Order #s 1,2,3,4,5...) 

Về cơ bản, tôi sẽ cần phải tự làm công việc của identity chức năng SQL kể từ khi số lượng khách hàng là không giới hạn và tôi cần order # quầy cho mỗi người trong số họ.

Tôi đang làm khá thoải mái:

BEGIN TRANSACTION 
    SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
    INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

Vấn đề của tôi là khóa và mối quan tâm đồng thời và đảm bảo một giá trị duy nhất. Có vẻ như chúng ta cần khóa với TABLOCKX. Nhưng đây là một cơ sở dữ liệu khối lượng lớn và tôi không thể chỉ khóa toàn bộ bảng Orders mỗi lần tôi cần thực hiện quy trình SELECT MAX+1 và chèn một bản ghi thứ tự mới.

Nhưng, nếu tôi không khóa toàn bộ bảng, thì tôi có thể không nhận được giá trị duy nhất cho khách hàng đó. Bởi vì một số mục nhập của chúng tôi được thực hiện sau khi thực tế theo lô bởi một quy trình Windows đa luồng, có thể là 2 hoạt động sẽ đồng thời muốn chèn một đơn đặt hàng mới cho cùng một khách hàng.

Vậy phương pháp hoặc kỹ thuật khóa nào sẽ tránh được các khóa chết và vẫn cho phép tôi duy trì số thứ tự tăng dần duy nhất PER khách hàng?

+0

thể trùng lặp của [sql server: là làm tổ này trong Transcation đủ để có được một số duy nhất từ ​​cơ sở dữ liệu?] (http://stackoverflow.com/questions/4169591/sql-server-is-this-nesting-in-a-transcation-sufficient-for-getting-a-unique-num) – GSerg

Trả lời

0

có thể tạo bảng có trường IDENTITY cho từng khách hàng không, sau đó bạn có thể chèn bản ghi mới vào bảng của khách hàng và kéo giá trị từ đó.

+0

Antony , Tôi không thể tạo các trường IDENTITY riêng biệt cho từng khách hàng vì số lượng khách hàng lớn và luôn tăng lên. – stackonfire

7

Tôi sẽ giới thiệu một bảng để giữ số cuối mỗi khách hàng để truy vấn và cập nhật nó trong cùng một giao dịch với việc tạo đơn hàng.

TABLE CustomerNextOrderNumber 
{ 
    CustomerID id PRIMARY KEY, 
    NextOrderNumber int 
} 

Khóa cập nhật khi chọn sẽ giúp tránh tình trạng cuộc đua khi hai đơn hàng được đặt đồng thời bởi cùng một khách hàng.

BEGIN TRANSACTION 

DECLARE @NextOrderNumber INT 

SELECT @NextOrderNumber = NextOrderNumber 
FROM CustomerNextOrderNumber (UPDLOCK) 
WHERE CustomerID = @CustomerID 

UPDATE CustomerNextOrderNumber 
SET NextOrderNumber = NextOrderNumber + 1 
WHERE CustomerID = @CustomerID 


... use number here 


COMMIT 

tương tự, nhưng đơn giản hơn cách tiếp cận (lấy cảm hứng từ Joachim Isaksson) cập nhật khóa ở đây là áp đặt bởi bản cập nhật đầu tiên.

BEGIN TRANSACTION 

DECLARE @NextOrderNumber INT 

UPDATE CustomerNextOrderNumber 
SET NextOrderNumber = NextOrderNumber + 1 
WHERE CustomerID = @CustomerID 

SELECT @NextOrderNumber = NextOrderNumber 
FROM CustomerNextOrderNUmber 
where CustomerID = @CustomerID 

...

COMMIT 
+2

hoặc sử dụng "cập nhật CustomerNextOrderNumber đặt NextOrderNumber = NextOrderNumber + 1 đầu ra chèn.NextOrderNumber nơi Customerid =?" –

+0

@Joachim Isaksson Có, điều này cũng sẽ hoạt động .. – alexm

+0

@Joachim Isaksson: Sau khi nghĩ về nó, tôi thích cách tiếp cận của bạn hơn :) – alexm

2

Mức giao dịch mặc định, read committed, không bảo vệ bạn chống lại ma đọc. Một phantom đọc là khi quá trình khác chèn một hàng ở giữa select của bạn và insert: cao

BEGIN TRANSACTION 
SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

Ngay cả một cấp, đọc lặp lại, không bảo vệ bạn. Chỉ có mức cách ly cao nhất, có thể tuần tự hóa, bảo vệ chống lại các lần đọc ma.

Vì vậy, một giải pháp là mức cô lập cao nhất:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRANSACTION 
... 

Một giải pháp khác là sử dụng tablockx, holdlock and updlock table hints để đảm bảo chỉ giao dịch của bạn có thể sửa đổi các bảng. Đầu tiên khóa bảng, cái thứ hai giữ khóa cho đến khi kết thúc giao dịch, và cái thứ ba lấy một khóa update để chọn, vì vậy nó không phải nâng cấp sau này.

SELECT @NewOrderNumber = MAX(OrderNumber)+1 
From Orders with (tablockx, holdlock, updlock) 
where [email protected] 

Các truy vấn này sẽ nhanh chóng nếu bạn có một chỉ mục trên CustomerID, vì vậy tôi sẽ không lo lắng quá nhiều về đồng thời, chắc chắn không phải nếu bạn có ít hơn 10 đơn đặt hàng mỗi phút.

+0

Andomar, cảm ơn bạn đã trả lời. Trong quá trình kiểm tra căng thẳng, chúng tôi đã nhận được quá nhiều lần bế tắc khi kiểm tra SERIALIZABLE. Đó là lý do tại sao chúng tôi đã thử TABLOCKX. Chúng tôi đã không kiểm tra với HOLDLOCK. Chúng tôi thực sự sẽ có một chỉ mục trên CustomerID nhưng biết rằng chúng tôi sẽ có> 10 đơn đặt hàng mỗi giây khi nhập hàng loạt đơn đặt hàng trong quá trình xử lý hàng loạt sử dụng nhiều luồng và một số sẽ có cùng CustomerID. – stackonfire

+0

Cũng 'tablockx' không có' holdlock' mở cửa cho deadlocks: khóa bảng sẽ được giải phóng giữa 'select' và' update'. Vì vậy, hãy thử kiểm tra bằng 'holdlock'. Bạn cũng có thể thử nghiệm bằng gợi ý truy vấn 'updlock', mặc dù' tablockx' đã bao gồm lãnh thổ đó. – Andomar

3

Bạn có thể làm điều này:

BEGIN TRANSACTION 
    SELECT ID 
    FROM Customer WITH(ROWLOCK) 
    WHERE Customer.ID = @ID 

    SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
    INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

Chúng tôi đang bây giờ chỉ khóa một khách hàng từ bảng khách hàng và không phải tất cả khách hàng, bất cứ khi nào 2 người cố gắng thêm một trật tự cho cùng một khách hàng cùng một lúc, bất cứ ai nhận khóa trên chiến thắng đầu tiên của khách hàng và người kia sẽ phải đợi.

Nếu mọi người đang chèn đơn đặt hàng cho các khách hàng khác nhau, họ sẽ không nhận được theo cách khác!

Sau đây là cách này sẽ làm việc:

  • User1 bắt đầu để chèn một trật tự cho khách hàng với ID 1000.
  • User2 cố gắng để chèn một trật tự cho khách hàng với ID 1000.
  • User2 phải đợi cho đến khi User1 hoàn tất việc chèn thứ tự.
  • Người dùng1 chèn thứ tự và giao dịch được thực hiện.
  • User2 bây giờ có thể chèn thứ tự và được đảm bảo để có được sự thật tối đa cho khách hàng OrderID 1000.
+0

Cảm ơn bạn. Bạn có thể giải thích lý do tại sao bạn cần truy vấn đầu tiên của bạn (chọn ID từ Khách hàng với (rowlock), nơi [email protected]) ở tất cả? Mối quan tâm khóa là trên bảng đơn đặt hàng và thậm chí là câu lệnh SELECT chỉ giới hạn trong một ID khách hàng. – stackonfire

+0

@stackonfire, tôi thêm một số giải thích thêm, cho tôi biết nếu nó vẫn không rõ ràng. –

0

Bạn đang cố gắng liên hệ hai yêu cầu hoàn toàn khác nhau.

Thậm chí nếu bạn làm việc này. Điều gì sẽ xảy ra nếu Khách hàng A có đơn đặt hàng được xoá, bạn sẽ đổi lại tất cả các hồ sơ hiện có của họ để giữ cho chúng liên tục và bắt đầu từ 1. Bây giờ sẽ là một vấn đề khóa ....

Ghi lại danh tính (hoặc có thể là một guid) Khi bạn muốn đếm, truy vấn cho nó, nếu bạn muốn số hàng (không bao giờ nhìn thấy điểm của bản thân mình), sử dụng rowno.

Bạn không cần thứ tự tăng dần tự động cho mỗi khách hàng, bạn không muốn một đơn đặt hàng và không có số lượng khóa lớn không thể có.

Thời gian suy nghĩ bên.

Nếu bạn giới thiệu

Order Description Date Due 
1  Staples  26/1/2012 
2  Stapler  1/3/2012 
3  Paper Clips 19/1/2012 

nó không có nghĩa (và trên thực tế không nên có ý nghĩa) mà các phím theo thứ tự là 1, 2 và 3, họ có thể là bất cứ điều gì miễn là họ hoàn thành một độc đáo yêu cầu.

+0

Tony, cảm ơn bạn đã trả lời. Chúng tôi sẽ không xóa số đơn đặt hàng trước đó. Tôi đồng ý rằng tình trạng này không phải là lý tưởng, so với chỉ sử dụng danh tính hoặc ID khác sql, nhưng logic kinh doanh đòi hỏi phương pháp này. Các chi tiết thực tế phức tạp hơn những gì tôi đã đăng, tất nhiên. Lý do thực sự là những lý do tốt. Và khách hàng phải tham khảo ID đơn hàng duy nhất của họ theo số, vì vậy tôi chỉ có thể đếm chúng. Dù sao, câu hỏi vẫn còn rất thú vị trong lý thuyết bất kể tôi có thể tranh luận về logic kinh doanh hay không. – stackonfire

+1

Không cần nó, tách thời gian quan tâm. Bạn đang làm cho lớp cơ sở dữ liệu của bạn trở nên phức tạp, dễ vỡ và tốn kém đơn giản để khách hàng có thể tham khảo một đơn đặt hàng bằng khóa của nó. Surrogates là con đường để đi và nếu mở rộng quy mô là một vấn đề sau đó sử dụng một Guid, đó là những gì họ đang có. Điều duy nhất thú vị về câu hỏi này là tần suất mọi người rơi vào cái bẫy này. –

0
create table TestIds 
(customerId int, 
nextId int) 

insert into TestIds 
values(1,1) 
insert into TestIds 
values(2,1) 
insert into TestIds 
values(3,1) 

go 

create proc getNextId(@CustomerId int) 
as 

declare @NextId int 

while (@@ROWCOUNT = 0) 
begin 
    select @NextId = nextId 
    from TestIds 
    where customerId = @CustomerId 

    update TestIds 
    set nextId = nextId + 1 
    where customerId = @CustomerId 
    and nextId = @NextId 

end 

select @NextId 
go 
3

Trong SQL Server 2005 và sau đó, đây là cố gắng hết sức nguyên tử, mà không sử dụng bất kỳ giao dịch hoặc khóa:

update ORDERS 
set OrderNumber=OrderNumber+1 
output inserted.OrderNumber where [email protected] 
Các vấn đề liên quan