Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql “创建自定义”;“自动递增”;复合主键?_Sql_Sql Server - Fatal编程技术网

Sql “创建自定义”;“自动递增”;复合主键?

Sql “创建自定义”;“自动递增”;复合主键?,sql,sql-server,Sql,Sql Server,我有一组父子表(1对多关系)。我正在构建表格,对PKs和auto increment的使用有一些疑问 父表有一个自动编号PK(用于存储销售单标题)。这里的一张唱片意味着在票上 子表用于存储票据详细信息。此处的一条记录是票据中的一个行项目(例如焦炭、火星棒等) 我知道子表的PK应该有两个字段: 父表的主键 使行项目在此票据中唯一的编号 如果我使用IDENTITY,它将不会在家长PK更改后“重新启动” 我将用一个例子来说明: create table dbo.tOrders ( OrderI

我有一组父子表(1对多关系)。我正在构建表格,对PKs和auto increment的使用有一些疑问

父表有一个自动编号PK(用于存储销售单标题)。这里的一张唱片意味着在票上

子表用于存储票据详细信息。此处的一条记录是票据中的一个行项目(例如焦炭、火星棒等)

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

  • 父表的主键
  • 使行项目在此票据中唯一的编号
  • 如果我使用
    IDENTITY
    ,它将不会在家长PK更改后“重新启动”

    我将用一个例子来说明:

    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
    
    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在Col1发生变化后复位;由Col2组成的Col1是唯一的


    SQL Server是否实现了密钥的这种使用?或者我需要编码吗?

    您没有一对多关系。 你有一种多对多的关系。 父项可以有许多项。 可口可乐可以属于多个父公司

    你想要三张桌子。中间表有时称为连接表

    注意:在wiki文章中,它们只显示连接表中的两列,我认为最好的做法是该表也有一个唯一的自动递增字段


    注意:这两个连接字段通常是一个唯一的索引。

    您必须自己为此编写逻辑代码。您可以通过触发器实现任务,并使用窗口函数(row_number()over(partitionby parent_id order by…)来简化任务

    您还可以让主键只是一个标识列(父\u id不必是PK的一部分),并有一个“Sequence\u Num”列来跟踪要用每个父\u id重置的int。您甚至可以这样做,并且仍然可以在父\u id/Sequence\u Num cols上设置聚集索引

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

    举个例子:

    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
    
    困难在于允许多次插入和延迟插入。 嗯

    另一个选项是使用替代触发器:

    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设置为“NOTNULL”因为多次插入会导致一个重复的键。因此,我不能使用主键,而是使用聚集索引。

    您需要对其进行编码,但没有快捷方式。正如其他人所说,没有直接的实现方法。您需要使用桥接表或编写一些代码来强制执行此要求。您可能需要考虑者:您不需要像这样存储数据。如果您只需要票据上的项目编号,则可以将其作为显示问题而不是存储问题来解决。查看SQL中的窗口函数,如:
    ROW\u NUMBER()OVER(按Col1分区,按Col1、Col2排序)
    连接表不需要额外的自动增量列。这是完全无用的,因为它已经有了主键。我没有说连接表需要自动增量列。我说有一个是好的。事实上,许多gui自动绑定控件都需要一个。而且,使用一个字段t删除/更新记录要容易得多han必须始终指定这两个字段。在某些商店,这是一种强制性做法。