1

我有两个表,我们将它们称为FooBar,具有一对多的关系,其中Foo的父级是Bar。Foo 的主键是一个随序列自动生成的整数。

因为Bar完全取决于Foo我将如何设置Bar给定以下约束的主键:

  • Bar 的记录是以编程方式生成的,因此不能依赖用户输入作为标识符。
  • 多个进程正在生成 Bar 记录,因此任何涉及 a Select Max()来生成 anID都会呈现竞争条件。

我提出了两种我不满意的可能解决方案:

  • 将这些表视为与第三个表的多对多关系,第三个表将它们的记录映射在一起,并让应用程序代码处理插入记录,以便正确创建记录之间的映射。我不喜欢这样,因为它会使数据库设计产生误导,并且应用程序代码中的错误可能会导致无效数据。
  • 给 Bar 两个列:FooIDand 并通过选择 for someFooBarID生成一个值 ,但如前所述,这会产生竞争条件。FooBarIDmax(FooBarID)+1FooID

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

4

4 回答 4

6

给 Bar 一个与 Foo 相同的自动主键。将外键 FooID 列添加到 Bar。

除非我遗漏了什么,否则似乎没有理由让它不起作用。

于 2008-12-10T18:28:14.023 回答
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()

于 2008-12-10T18:36:30.027 回答
0

根据您的描述,我假设您的数据库不支持自动增量标识符字段(MS SQL 支持,Oracle 有“序列”,即使不是更好,也一样好,我不记得 MySql 有)。

如果是这样,那么您只需要一个自增的 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 的解决方案就是必要的。如果有人使用不支持身份或序列构造的数据库,我将把这个答案作为一个例子来说明如何做到这一点

于 2008-12-10T18:33:09.537 回答
0

根据 Ant P 和其他答案,我也不太明白为什么只为 bar 生成唯一 ID 并将 Foo 的 ID 放入其中是行不通的。但是假设您处于自动递增 ID 不可用的情况,那么有两种解决方案不涉及选择 max(barid)+1

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

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

于 2008-12-10T18:43:42.510 回答