4

自動インクリメントする主キーOrderHeaderIDがありますが、注文先の会社に基づいてOrderNumberも自動インクリメントする必要があります。

現在の戦略:(テーブルへの挿入ステートメントについて

(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
4

3 に答える 3

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このルーチンの別のインスタンスが同時に実行されなくなります。代わりに、元のプロセスがINSERTandを実行するまで、2 番目のインスタンスはブロックされますCOMMIT。その時点で、2 番目のインスタンスは、新しく生成された値の読み取りとロックに進みます。

于 2012-09-13T20:42:05.193 に答える
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 )

同時実行(競合)条件はありません。「連動更新」がそれを処理します。さらに、データベース設計を非正規化していません (最初の正規形では、すべての行と列の交差がアトミック/非分解可能である必要があります。適用可能なドメインからの値が 1 つだけ含まれ、他には何も含まれていません。あなたのような複合識別子は禁止されています。)

簡単!

于 2012-09-13T21:18:00.960 に答える
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
于 2012-09-13T21:56:35.920 に答える