一对多关系中的标识符

发布于 2024-07-09 18:04:33 字数 774 浏览 9 评论 0原文

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

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

  • Bar 的记录以编程方式进行 生成的用户输入不能 依赖于标识符。
  • 多个进程正在生成 酒吧记录所以任何涉及 选择 Max() 生成 ID 将 提出竞争条件。

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

  • 将表格视为一个 与 a 的多对多关系 第三个表映射他们的记录 一起并申请 代码处理插入记录所以 记录之间的映射 已正确创建。 我不喜欢 这是因为它使得数据库设计 应用中的误导和错误 代码可能会导致无效数据。
  • 给 Bar 两列:FooIDFooBarID 并生成一个值 FooBarID 通过选择 对于某些 FooIDmax(FooBarID)+1,但是 如前所述,这会创建一个 竞赛条件。

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

I have two tables, we'll call them Foo and Bar, with a one to many relationship where Foo is the parent of Bar. Foo's primary key is an integer automatically generated with a sequence.

Since Bar is fully dependent on Foo how would I setup the primary key of Bar given the following constraints:

  • Records for Bar are programatically
    generated so user input can not be
    relied upon for an identifier.
  • Multiple processes are generating
    Bar records so anything involving a
    Select Max() to generate an ID would
    present a race condition.

I have come up with two possible solutions that I am not happy with:

  • Treat the tables as if they are a
    many to many relationship with a
    third table that maps their records
    together and have the application
    code handle inserting records so
    that the mapping between the records
    is created correctly. I don't like
    this as it makes the database design
    misleading and errors in application
    code could result in invalid data.
  • Give Bar two colunms: FooID and
    FooBarID and generate a value for
    FooBarID by selecting the
    max(FooBarID)+1 for some FooID, but
    as previously stated this creates a
    race condition.

I appreciate any ideas for an alternative table layout.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

您的好友蓝忘机已上羡 2024-07-16 18:04:33

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

除非我错过了一些东西,否则似乎没有理由它不起作用。

Give Bar an automatic primary key the same as with Foo. Add a foreign key FooID column to Bar.

Unless I'm missing something, there doesn't seem to be a reason why it wouldn't work.

抠脚大汉 2024-07-16 18:04:33

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

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() 在会话中返回的内容。

Unless I'm missing something in your description, this sounds like an ordinary case. The usual solution is something like this:

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');

The CURRVAL() function of a sequence only returns the most recent value generated by that sequence during your current session. Other concurrent use of that sequence doesn't affect what CURRVAL() returns in your session.

傲世九天 2024-07-16 18:04:33

根据您的描述,我假设您的数据库不支持自动增量标识符字段(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 的解决方案就是必需的。 我将此答案作为一个示例,说明如果有人使用不支持身份或序列构造的数据库,如何执行此操作

from your description i'm assuming that your database does not support auto-increment identifier fields (MS SQL does, Oracle has 'sequences' which are just as good if not better, I don't remember MySql has).

If it does, then all you need is an auto-increment FooId and an auto-increment BarId, and Bar also has a FooId as a foreign key

If it does not, then you can create a single-row table for allocation as follows:

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

note that if your database does not support the syntax

@BarId = BarIdAllocator = BarIdAllocator + 1

then you'll need to do it this way instead

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

EDIT: I missed the Oracle tag originally, so Bill's solution is all that is necessary. Am leaving this answer as an example of how to do it in case someone is using a database that does not support identity or sequence constructs

℡Ms空城旧梦 2024-07-16 18:04:33

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

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

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

I can't quite see either, as per Ant P's and other answers, why which just generating a unique ID for bar and dropping Foo's ID in won't work. But supposing you're in a situation where auto-incrementing IDs are not available then there's two solutions that do not involve selecting max(barid)+1

  1. Pre-generate a table of unique IDs and use a transaction to pull the next available ID from the table and delete it (as an atomic operation). This works fine but has the disadvantage that you have to keep the table populated.

  2. Generate a UUID as the primary key. This isn't generally a good option as UUIDs are inefficient for this use, but it does have the advantage that no additional infrastructure tables are needed. UUID generators are widely available and some databases have them built in.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文