一对多关系中的标识符

时间:2008-12-10 18:21:04

标签: database oracle database-design ddl

我有两个表格,我们称之为FooBar,其中FooBar的父亲之间的一对多关系。 Foo的主键是使用序列自动生成的整数。

由于Bar完全依赖于Foo,如果给出以下约束,我将如何设置Bar的主键:

  • Bar的记录是以编程方式进行的 生成所以用户输入不能 依靠标识符。
  • 正在生成多个进程 酒吧记录所以涉及的任何事情 Select Max()生成一个ID会 提出竞争条件。

我想出了两个我不满意的可能解决方案:

  • 将表视为一个表 与...有多对多的关系 映射其记录的第三个表 一起并有应用程序 代码句柄插入记录所以 记录之间的映射 是正确创建的。我不喜欢 这是因为它使数据库设计 误导和应用中的错误 代码可能导致无效数据。
  • 给Bar两个colunms:FooIDFooBarID并生成一个值 FooBarID选择。{ 某些max(FooBarID)+1的{​​{1}},但是 如前所述,这创造了一个 竞争条件。

我很欣赏任何替代表格布局的想法。

4 个答案:

答案 0 :(得分:5)

为Bar提供与Foo相同的自动主键。将外键FooID列添加到Bar。

除非我遗漏某些东西,否则似乎没有理由说它不起作用。

答案 1 :(得分:3)

除非我在您的描述中遗漏了某些内容,否则这听起来像是一个普通的案例。通常的解决方案是这样的:

INSERT INTO Foo (foo_id, othercolumn)
  VALUES ( FooSeq.NextVal(), 'yadda yadda');

INSERT INTO Bar (bar_id, foo_id, extracolumn)
  VALUES ( BarSeq.NextVal(), FooSeq.CurrVal(), 'blah blah');
INSERT INTO Bar (bar_id, foo_id, extracolumn)
  VALUES ( BarSeq.NextVal(), FooSeq.CurrVal(), 'bling bling');
INSERT INTO Bar (bar_id, foo_id, extracolumn)
  VALUES ( BarSeq.NextVal(), FooSeq.CurrVal(), 'baz baz');

序列的CURRVAL()函数仅返回当前会话期间序列生成的最新值。其他并发使用该序列不会影响会话中CURRVAL()返回的内容。

答案 2 :(得分:0)

从你的描述中我假设你的数据库不支持自动增量标识符字段(MS SQL确实如此,Oracle有'序列',如果不是更好,那就好了,我不记得MySql了。) / p>

如果是,那么您只需要一个自动增量FooId和一个自动增量BarId,而Bar也有一个FooId作为外键

如果没有,那么您可以创建一个单行表进行分配,如下所示:

create table SystemCounter 
( 
    SystemCounterId int identity not null, 
    BarIdAllocator int 
)
--initialize SystemCounter to have one record with SystemCounterId = 1
--and BarIdAllocator = 0
insert into SystemCounter values (1,0)
--id allocator procedure
create procedure GetNextBarId ( @BarId int output ) AS
    SET NOCOUNT ON
    begin tran
        update SystemCounter set 
            @BarId = BarIdAllocator = BarIdAllocator + 1
        where SystemCounterId = 1
    commit
GO

请注意,如果您的数据库不支持语法

@BarId = BarIdAllocator = BarIdAllocator + 1

然后你需要这样做而不是

begin tran
    update SystemCounter set 
        BarIdAllocator = BarIdAllocator + 1
    where SystemCounterId = 1
    select 
        @BarId = BarIdAllocator
    from SystemCounter
    where SystemCounterId = 1
commit

编辑:我最初错过了Oracle标签,所以Bill的解决方案就是必要的。如果有人使用不支持身份或序列构造的数据库,请留下此答案作为如何执行此操作的示例

答案 3 :(得分:0)

根据Ant P和其他答案,我不能完全看到为什么只为bar生成一个唯一的ID并丢弃Foo的ID将无效。但假设您处于自动递增ID不可用的情况,那么有两个解决方案不涉及选择max(barid)+1

  1. 预先生成一个唯一ID表,并使用事务从表中提取下一个可用ID并将其删除(作为原子操作)。这样可以正常工作,但缺点是必须保持表格填充。

  2. 生成UUID作为主键。这通常不是一个好的选择,因为UUID对于此用途来说效率低,但它确实具有不需要额外的基础结构表的优点。 UUID生成器可以广泛使用,一些数据库内置它们。