2

上一个问题中,我对在 nm 关系中具有复合主键有了一些想法。我的情况非常相似,但又有些不同。

[Person]

    PERSON_ID: int, primary key
    CLIENT_ID: int, primary key
    Name: nvarchar(xx)
    primary_key( PERSON_ID, CLIENT_ID);

[Group]

    GROUP_ID: int
    CLIENT_ID: int
    Name: nvarchar(xx)
    primary_key( GROUP_ID, CLIENT_ID);

现在,当我使用 mysql-workbench 工具生成关系表时,它的作用是创建一个表:

选项1:

    [PersonHasGroup]

    PERSON_ID: int 
    CLIENT_ID: int 
    GROUP_ID: int
    CLIENT1_ID: int
primary_key( PERSON_ID, CLIENT_ID, GROUP_ID, CLIENT1_ID);

在我的情况下,两个 client_id 通常具有相同的值,所以我将表格编辑为看起来像

选项2:

    [PersonHasGroup]

    PERSON_ID: int
    CLIENT_ID: int
    GROUP_ID: int
primary_key( PERSON_ID, CLIENT_ID, GROUP_ID);

这些是好的做法吗?我的其他同事喜欢的有点不同。他们使用:

选项 3:

   [PersonHasGroup]
    PERSON_HAS_GROUP_ID: int, auto-increment
    CLIENT_ID: int        
    PERSON_ID: int, foreign key
    GROUP_ID: int, foreign key
primary_key( PERSON_HAS_GROUP_ID, CLIENT_ID);

当我的关系是多对多的例子时,哪种做法是合适的:)

4

5 回答 5

3

所提出的选项之间不应有任何辩论。有一个正确答案,但该答案取决于您的业务规则。

您的陈述“在我的情况下,两个 client_id 通常具有相同的值”让我感到担忧。像通常这样的术语在设计系统时通常无济于事。你需要从绝对的角度来谈论。

如果两个关系的 CLIENT_ID 必须相同,则选项 2(一个单个 CLIENT_ID)是正确的设计。

但是,如果每个关系都可以基于不同的 CLIENT_ID 是可能的(可能很少见,但可能),那么您当然需要具有两个唯一命名的 CLIENT_ID 列的选项 1。(我无法想象这个商业案例,但你的模型已经很奇怪了,所以我不太确定)

除非您完全解释您的业务需求,否则没有人可以帮助您确定哪个是正确的,但您指出这是不可能的。

选项 3 不应被视为一个选项 - 如果您要引入代理键,那么它不应该是复合代理键。

我个人永远不会为联结表(解决多对多关系的表)创建代理键,除非联结表 PK 可以是另一个表中的外键。在这种情况下,出于性能原因,我可能会创建一个代理键,但这不是一个简单的选择。如果创建了代理键,则应同时声明主键和备用键:一个使用自然复合键,另一个使用代理键。您希望确保给定的组/人员对只输入一次,并且您希望代理也是唯一的。

因此,如果您的特定情况出于性能原因需要它,则可以修改选项 1 和 2 以包含代理键:

选项 1A

[PersonHasGroup]
  PERSON_ID: int
  PERSON_CLIENT_ID: int
  GROUP_ID: int
  GROUP_CLIENT_ID: int
  PERSON_HAS_GROUP_ID: int
primary_key( PERSON_ID, PERSON_CLIENT_ID, GROUP_ID, GROUP_CLIENT_ID);
alternate_key( PERSON_HAS_GROUP_ID )

选项 2A

[PersonHasGroup]
  PERSON_ID: int
  CLIENT_ID: int
  GROUP_ID: int
  PERSON_HAS_GROUP_ID: int
primary_key( PERSON_ID, CLIENT_ID, GROUP_ID);
alternate_key( PERSON_HAS_GROUP_ID )

当然,您可以颠倒哪个是主要的,哪个是备用的。

于 2012-08-16T03:52:00.160 回答
2

我更喜欢使用单个 id 以便于引用,以及唯一对的另一个唯一键,从未使用过工作台,所以这里首先在普通 sql 中:

CREATE TABLE person_has_group (
    person_has_group_id SERIAL,
    client_id BIGINT UNSIGNED NOT NULL REFERENCES clients (client_id),
    person_id BIGINT UNSIGNED NOT NULL,
    group_id BIGINT UNSIGNED NOT NULL,
    FOREIGN KEY (client_id, person_id) REFERENCES persons (client_id, person_id),
    FOREIGN KEY (client_id, group_id) REFERENCES groups (client_id, group_id),
    UNIQUE KEY (client_id, person_id, group_id)
);

在“工作台”中类似这样的东西:

[PersonHasGroup]
    PERSON_HAS_GROUP_ID: int, auto-increment
    CLIENT_ID: int        
    PERSON_ID: int, foreign key
    GROUP_ID: int, foreign key
    primary_key( PERSON_HAS_GROUP_ID)
    unique_key( CLIENT_ID, PERSON_ID, GROUP_ID);
于 2012-08-08T11:51:29.203 回答
1

如果我理解正确,以下应该是正确的:

  • 您有多个服务器 (DB),每个客户端一个。

  • 每个 DB 都有自己的PersonID和的自动增量GroupID

  • 您试图简单地(直接)将所有这些导入到一个多租户数据库中;因此和表ClientID的主键。PersonGroup

如果这些陈述不正确,请忽略其余陈述。

在此处输入图像描述

create table PersonGroup (
    PersonID integer
  , GroupID  integer
  , ClientID integer
);

alter table PersonGroup add constraint  pk_PersonGroup
            primary key (PersonID, GroupID, ClientID);

alter table PersonGroup add constraint fk1_PersonGroup
            foreign key (PersonID, ClientID) references Person(PersonID, ClientID);

alter table PersonGroup add constraint fk2_PersonGroup
            foreign key (GroupID, ClientID) references Group(GroupID, ClientID);
于 2012-08-16T20:42:15.677 回答
0

好问题。但让我们先快速浏览一下您的PersonGroup表。

考虑到表的名称,Person中的每一行似乎代表一个Group中的每一行似乎代表一个group。所以从概念上讲,要识别一个人或组,您不需要Client_ID,因此您不需要它作为主键的一部分。如果您的模型要求每个人都有一个唯一的Client_ID(除了您的Person_ID 之外),只需将其设为唯一字段,但不要将其包含在主键中。

表的情况有点混乱。问题是为什么不能单独通过Group_ID识别一个组?(Client_ID字段将组与客户端相关联。但是这种关系的本质是什么?每个组是由客户端创建的吗?在这种情况下,您不需要将Client_ID作为键的一部分,您只需要将其作为外键。它是成员关系吗?在这种情况下,您需要一个由另一个表表示的 N 到 M 关系)。

然后我们得到PersonGroup之间的关系。一个 N 到 M 关系总是被翻译成一个表,它由两个表的 PK + N 到 M关系的任何属性(例如成员资格的日期)组成。在这种情况下,您只需要一个包含Person_IDGroup_ID两个字段的表,并且它们都是复合键的一部分。

您还必须小心命名.. PersonH​​asGroup代表什么?该人是否拥有该组?管它吗?或者它只是一个会员资格。如果它是一个成员,那么像“ PersonGroup ”这样简单的东西就足够了。

确保您在概念上正确地看到实体,否则您将陷入将字段添加到主键并链接不能完全满足您需求的表的陷阱。将实体的属性封装在其中,不要混合它们。问问自己“我如何识别现实世界中的实体? ” .. 你就知道主键应该是什么了。诸如“在我的情况下,两个 client_id 通常具有相同的值”之类的内容表明您没有正确看到概念并将它们混合在一起,从而导致冗余和不正确的关系。

最后,我了解有时会交给您一个设计不佳的数据库,并要求您引入新的链接和实体。我的建议是尽可能地进行更正。从长远来看,它会拯救你。

因此,在我看来,与此问题中的设计相比,您的先前设计要好得多。

于 2012-08-16T19:08:26.263 回答
0

我认为option 2会更好,因为它还添加UNIQUE CONSTRAINT了列( PERSON_ID, CLIENT_ID, GROUP_ID),这样就不会将重复项插入到您的表中。

于 2012-08-08T11:41:45.060 回答