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

Sql 自动递增订单号(一个表中有多个公司),sql,sql-server,auto-increment,Sql,Sql Server,Auto Increment,我有一个主键OrderHeaderID,可以自动递增,但我还需要根据下订单的公司自动递增OrderNumber 当前策略:(在我的insert语句中) 问题:一旦我开始使用某些卷进行测试,我就开始出现重复的密钥错误: 表OrderHeader: OrderHeaderID CompanyID OrderNumber 1 1 10000 2 1 10001 3 1 10002

我有一个主键OrderHeaderID,可以自动递增,但我还需要根据下订单的公司自动递增OrderNumber

当前策略:(在我的insert语句中)

问题:一旦我开始使用某些卷进行测试,我就开始出现重复的密钥错误:

表OrderHeader:

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

4             2         10000
5             2         10001
6             2         10002

要解决并发问题,您必须提高
SELECT
语句的隔离级别,然后在同一事务中执行
INSERT

每个RBDM的具体语法都会有所不同,但下面是一个使用Sql Server的示例:

BEGIN TRANSACTION
    DECLARE @OrderNumber INT

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

    INSERT... (@OrderNumber)
COMMIT

这将对在
SELECT
语句期间读取的行设置独占锁,这将阻止此例程的另一个实例同时运行。相反,第二个实例将被阻止,直到原始进程执行
插入
提交
,此时第二个实例将继续读取并锁定新生成的值。

请看我的回答:

出于您的目的,您可以这样定义公司表:

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 ) ,
)
您的订单表如下:

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 )
您没有并发(争用)条件:“互锁更新”会处理这个问题。此外,您还没有对数据库设计进行非规范化(第一种标准形式要求每一行和列的交集都是原子的/不可分解的:它只包含一个来自适用域的值,而不包含任何其他值。像您这样的复合标识符是多余的。)


轻松点

我将使用一个函数来自动增加每个给定ID的订单号

这是我在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

您正在使用什么rdbms?sql server、mysql、oracle?为什么您希望订单号依赖于公司ID?我假设从他使用的SQL Server变量名。。。重新标记。谢谢,非常简单,没有任何架构更改!!此解决方案的问题在于它具有内置的竞争条件()。在UDF计算订单号和将该值插入表之间,另一个进程可以(并且在某个时候会)计算相同的订单号,并在进程完成插入之前将其插入表中。你会试图弄清楚为什么会出现看似随机的重复键错误(或者更糟的是,两个不同的顺序被合并成一个)。哦,我非常清楚这一点,也很清楚,这是本例答案的一半。事务和适当的隔离级别将是答案的其余部分,这显然是我的疏忽。