创建自定义“自动增量”复合主键?

时间:2012-04-03 04:00:29

标签: sql sql-server

我有一组父子表(1对多关系)。我正在构建表格,并对使用PK和自动增量有一些疑问。

父表具有自动编号PK(用于存储销售单标题)。这里的一条记录意味着票。

子表用于存储票证详细信息。这里的一个记录是票证中的一个行项目(例如可乐,火星酒吧等)

我知道子表的PK应该有2个字段:

  1. 父表的PK
  2. 使此故障单中的订单项唯一的数字
  3. 如果我使用IDENTITY,则在父亲的PK更改后不会“重启”。

    我将用一个例子来展示它:

    A)SQL做什么

    Parent table
    Col1  Col2
    1     1000
    2     2543
    3     3454
    Note: Col1 is IDENTITY
    
    Child Table
    Col1  Col2  Col3
    1     1     Coke
    1     2     Mars Bar
    2     3     Sprite
    3     4     Coke
    3     5     Sprite
    3     6     Mars Bar
    Note: Col1 is taken from Parent Table; Col2 is IDENTITY
    

    B)我想要实现的目标

    Parent table is the same as above
    
    Child Table
    Col1  Col2  Col3
    1     1     Coke
    1     2     Mars Bar
    2     1     Sprite
    3     1     Coke
    3     2     Sprite
    3     3     Mars Bar
    

    注意:Col1取自父表; Col2更改后Col2重置;由Col2组成的Col1是唯一的。

    SQL Server是否实现了密钥的使用?或者我应该编码吗?

3 个答案:

答案 0 :(得分:3)

仅作为一个例子:

create table dbo.tOrders (
    OrderID int not null identity primary key,
    CustomerID int not null
);
create table dbo.tOrderPos (
    OrderID int not null foreign key references dbo.tOrders,
    OrderPosNo int null,
    ProductID int null
);
create clustered index ciOrderPos on dbo.tOrderPos
    (OrderID, OrderPosNo);
go
create trigger dbo.trInsertOrderPos on dbo.tOrderPos for insert
as begin
    update  opo
    set     OrderPosNo = isnull(opo2.MaxOrderPosNo,0) + opo.RowNo
    from    (select OrderID, OrderPosNo,
                    RowNo = row_number() over (partition by OrderID order by (select 1))
            from    dbo.tOrderPos opo
            where   OrderPosNo is null) opo
    cross apply
            (select MaxOrderPosNo = max(opo2.OrderPosNo)
            from    dbo.tOrderPos opo2
            where   opo2.OrderID = opo.OrderID) opo2
    where   exists (select * from inserted i where i.OrderID = opo.OrderID);
end;
go
declare @OrderID1 int;
declare @OrderID2 int;
insert into dbo.tOrders (CustomerID) values (11);
set @OrderID1 = scope_identity();
insert into dbo.tOrderPos (OrderID, ProductID)
values (@OrderID1, 1), (@OrderID1, 2), (@OrderID1, 3);
insert into dbo.tOrders (CustomerID) values (12);
set @OrderID2 = scope_identity();
insert into dbo.tOrderPos (OrderID, ProductID)
values (@OrderID2, 4), (@OrderID2, 5);
insert into dbo.tOrderPos (OrderID, ProductID)
values (@OrderID1, 6);
select * from dbo.tOrderPos;
go
drop trigger dbo.trInsertOrderPos;
drop table dbo.tOrderPos;
drop table dbo.tOrders;
go

困难在于允许多个插入和延迟插入。 HTH

另一种选择是使用替代触发器:

create trigger dbo.trInsertOrderPos on dbo.tOrderPos instead of insert
as begin
    insert into dbo.tOrderPos
            (OrderID, OrderPosNo, ProductID)
    select  OrderID,
            OrderPosNo =
            isnull( (select max(opo.OrderPosNo)
                    from    dbo.tOrderPos opo
                    where   opo.OrderID = i.OrderID), 0) +
            row_number() over (partition by OrderID order by (select 1)),
            ProductID
    from    inserted i;
end;

不幸的是,似乎无法将OrderPosNo设置为“not null”,因为多次插入会导致重复键。因此,我无法使用主键并改为使用聚簇索引。

答案 1 :(得分:1)

你没有一对多的关系。 你有多对多的关系。 父母可以有很多项目。 可乐可以属于多个父母。

你想要三张桌子。中间表有时称为联结表。

http://en.wikipedia.org/wiki/Junction_table

注意:在wiki文章中,他们只在联结表中显示两列,我相信最佳做法是该表还有一个唯一的自动递增字段。

注意:两个加入字段通常是唯一索引。

答案 2 :(得分:0)

您必须自己为此编写逻辑代码。您可以通过触发器实现它,并使用窗口函数(row_number()over(按父节点顺序分隔...)来使任务更容易。

您也可以让主键只是一个标识列(parent_id不成为PK的一部分),并且有一个“Sequence_Num”列来跟踪int您想要使用每个parent_id重置。您甚至可以执行此操作,并仍在parent_id / sequence_num cols上设置聚簇索引。

恕我直言,第二种选择更好,因为它允许更大的灵活性,没有任何重大缺点。它还使窗口函数更容易编写,因为您可以通过代理键(标识列)进行排序,以在重新生成sequence_num时保留插入顺序。在这两种情况下,您都必须自己管理“sequenec_num”列的排序。