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