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 COMMIT,此时第二个实例将继续读取并锁定新生成的值。

于 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 )

您没有并发(竞争)条件:“联锁更新”负责处理。此外,您还没有对数据库设计进行非规范化(第一种范式要求每个行和列的交集都是原子的/不可分解的:它只包含来自适用域的一个值,仅包含一个值。像您这样的复合标识符是禁止的。)

简单的!

于 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 回答