自动递增订单编号(一个表中有多个公司)

时间:2012-09-13 20:33:29

标签: sql sql-server auto-increment

我有一个自动递增的主键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

3 个答案:

答案 0 :(得分: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,此时第二个实例将继续读取并锁定新生成的值。

答案 1 :(得分: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 )

您没有并发(种族)条件:“互锁更新”负责这一点。此外,您还没有对数据库设计进行非规范化(第一种正常形式要求每个行和列的交集都是原子 /不可分解的:它只包含来自适用域的一个值而不包含任何其他值。像你这样的识别者是 verboten 。)

容易!

答案 2 :(得分: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