2012-09-13 32 views
4

我有一個主自動遞增的OrderHeaderID,但我也需要自動遞增OrderNumber基於什麼公司的訂單。自動遞增訂單號(多個公司在一個表中)

當前的戰略:(在我的INSERT語句表

(SELECT MAX(OrderNumber) + 1 FROM OrderHeader WHERE CompanyID = @CompanyID) 

問題:有一次,我帶些卷的開始測試,我開始重複鍵錯誤:

表OrderHeader:

OrderHeaderID CompanyID OrderNumber 
1    1   10000 
2    1   10001 
3    1   10002 

4    2   10000 
5    2   10001 
6    2   10002 
+1

你使用的是什麼rdbms? sql-server,mysql,oracle?你爲什麼要讓訂單號碼依賴於companyid? – Taryn

+0

我從變量名中假設他正在使用SQL Server ...重新標記。 –

+0

正確SQL Server – bbrian51

回答

3

要解決您的併發問題,您必須提高您的隔離級別SELECT語句,然後在同一個事務中執行INSERT

這樣做的具體語法將每個RBDMS有所不同,但在這裏是使用SQL Server的例子:

BEGIN TRANSACTION 
    DECLARE @OrderNumber INT 

    SELECT @OrderNumber = MAX(OrderNumber) + 1 
    FROM OrderHeader WITH (XLOCK) 
    WHERE CompanyID = @CompanyID 

    INSERT... (@OrderNumber) 
COMMIT 

這就對SELECT聲明中讀取行的排他鎖,這將阻止另一個實例這個例程從同時運行。相反,第二個實例將被阻塞,直到原始進程執行INSERTCOMMIT,此時第二個實例將繼續讀取並鎖定新生成的值。

+0

謝謝,非常簡單,沒有任何架構更改! – bbrian51

1

看看我的答案在這裏:sql server: generate primary key based on counter and another column value

你的目的,你可以這樣定義你的公司表:

create table dbo.company 
(
    id int   not null primary key , 
    name varchar(32) not null unique  , 
    order_counter not null default(0) , 
    ... 
) 

和訂單表這樣的:

create table dbo.order 
(
    company_id int not null foreign key references dbo.company(id) , 
    id   int not null , 
    order_number as 100000*company_id + id , 
    ... 
    constraint order_AK01 unique nonclustere (order_number ) , 
    constraint order_PK01 primary key clustered (company_id , id) , 
) 

而且設置您的「添加訂單」查詢:

declare @new_order_number int 

update dbo.company 
set @new_order_number = dbo.company.order_counter + 1 , 
    order_counter  = dbo.company.order_counter + 1 
where dbo.company.id = @some_company_id 

insert dbo.order (company_id , id) value (@some_company_id , @new_order_number) 

您沒有併發(競賽)條件:「互鎖更新」負責處理此問題。此外,您還沒有對數據庫設計進行非規範化處理(第一種常規形式要求每行和每一列交叉點都是原子 /不可分解的:它只包含來自適用域的一個值,而不包含其他值。 )

簡單!

0

我會使用一個函數來自動遞增給定ID的OrderNumber。

這是我在2分鐘內放在一起。它做你在問什麼。

create table dbo.OrderHeader (
    OrderHeader int identity(1,1) primary key 
    ,CompanyID int 
    ,OrderNumber int 
    ) 
go 

create function dbo.NextOrderNumber (
    @CompanyID int 
    ) 
returns int 
as 
begin 
    declare @result int; 

    select @result = OrderNumber + 1 
    from OrderHeader 
    where CompanyID = @CompanyID 

    if @result is null 
     set @result = 10000 

    return @result; 
end 
go 

insert into OrderHeader select 1, dbo.NextOrderNumber(1) 

insert into OrderHeader select 2, dbo.NextOrderNumber(1) 
insert into OrderHeader select 2, dbo.NextOrderNumber(1) 

insert into OrderHeader select 1, dbo.NextOrderNumber(1) 

select * 
from OrderHeader 
+0

這個解決方案的問題是它有一個內置的_race condition_(http://en.wikipedia。組織/維基/ Race_condition#計算)。在你的UDF對一個訂單號的計算和插入到表中的值之間,另一個進程可以(在某些時候會**和**)出現,計算*相同的*訂單號,並將其插入表之前你的過程完成插入。而且你還會試圖弄清楚爲什麼你會看到隨機重複鍵錯誤(或者更糟糕的是,兩個不同的命令會合併成一個)。 –

+0

噢,我非常清楚這一點,並且在這種情況下這也是答案的一半。交易和適當的隔離級別將是答案的其餘部分,這顯然是對我的忽視。 – NicVerAZ