Sql 使用属性建模1:Many关系

Sql 使用属性建模1:Many关系,sql,database-design,Sql,Database Design,我曾遇到过一个桌子的设计,它立刻让我觉得很奇怪,但现在我已经仔细考虑了它,我似乎无法想出一个我真正喜欢的设计 现有设计(简化)为: 虽然它看起来像是一个标准的多对多关系,但一个帐户实际上并不属于多个组。我立刻想,“好吧,我们可以把组id放入账户表中,这应该会起作用。”但接下来我该如何处理is\u primary属性呢 我可以将一个帐户id作为主帐户id放入组表中,然后我相信我可以在主帐户id、组id到帐户id、组id上使用外键强制RI 或者,我可以将“is_primary”标志移动到Accoun

我曾遇到过一个桌子的设计,它立刻让我觉得很奇怪,但现在我已经仔细考虑了它,我似乎无法想出一个我真正喜欢的设计

现有设计(简化)为:

虽然它看起来像是一个标准的多对多关系,但一个帐户实际上并不属于多个组。我立刻想,“好吧,我们可以把
组id
放入
账户
表中,这应该会起作用。”但接下来我该如何处理
is\u primary
属性呢

我可以将一个
帐户id
作为
主帐户id
放入
表中,然后我相信我可以在
主帐户id、组id到帐户id、组id
上使用外键强制RI

或者,我可以将“is_primary”标志移动到
Accounts
表中。也许这是最好的解决办法

对每种方法的利弊有何看法?我是否遗漏了任何潜在的问题?还有其他我错过的选择吗

在触发器(主要是声明性RI)之外的任何情况下,是否有任何方法在组内强制执行单个主帐户


谢谢

摆脱集团账户绝对是可能的

根据您的描述,似乎每个组都有许多帐户,但每个帐户只有一个组。因此,您可以按照建议将组id放入Accounts表中,然后将primary\u account\u id作为组中的一个字段。

关系基数 根据您的描述判断,您需要1:N关系,这意味着您不需要连接表
Group_Accounts
。只需从
帐户
进行一次简单的FK即可

专列 下一个问题是如何在N侧(
帐户
)选择一行作为“特殊”。您可以:

  • 使用
    Accounts.is_primary
    标志,并通过筛选的唯一索引(如果您的DBMS支持)强制执行其唯一性(每个组)
  • 或者您可以在
    组中有一个指向主帐户的FK。不过,在后一种情况下,您必须小心选择实际属于该组的主帐户
  • 第二种方法的建模与此类似:

    组。FK1
    表示:

    FOREIGN KEY (group_id, primary_account_no) REFERENCES Accounts (group_id, account_no)
    
    上述FK中存在
    group_id
    ,强制主帐户属于其主帐户所在的组

    创建新帐户时,请注意如何生成
    帐户\u no
    。您需要避免并发环境中的竞争条件(当然,DBMS会改变实际代码)


    如果您的DBMS支持过滤索引,并且没有具体的理由选择第二种方法,那么选择第一种方法

    如果出现以下情况,请选择第二个选项:

    • 您的DBMS不支持筛选索引
    • 或者您的DBMS支持延迟约束,您需要始终强制主帐户的存在(只需使
      primary\u account\u no
      notnull)
    • 或者您实际上不需要
      帐户\u id
      ,因此您可以减少一个索引(取决于DBMS对FKs索引的要求有多严格,以及您的实际工作负载,您可以避免在
      主帐户\u no
      上建立索引,而不是在
      is\u primary
      上必须存在的索引)

    通过将PK更改为帐户id而不是帐户和组,可以将m:n交叉表Group\U Accounts更改为1:n表。但是,您仍然会被强制执行一个且只有一个帐户是任何组的主帐户这一约束的额外开销所困扰

    但是,如果您将组FK移动到account记录,其中实际上应该是1:n基数,那么您可以创建一个类似于group_Accounts表的Primary_Accounts表,但PK将是组id。因此每个组只能有一个条目,这将是一个Primary account。它看起来是这样的:

    create table Groups (
        Id      int not null,
        Name    varchar( 50 ) not null,
        constraint PK_Groups primary key( Id )
    );
    
    create table Accounts (
        Id      int not null,
        Name    varchar( 50 ) not null,
        GroupID int not null,
        constraint PK_Accounts primary key( Id ),
        constraint FK_AccountGroup foreign key( GroupID )
            references Groups( ID )
    );
    
    create table PrimaryAccounts (
        GroupID      int not null,
        AccountID    int not null,
        constraint PK_PrimaryAccounts primary key( GroupId ),
        constraint FK_PrimaryGroup foreign key( GroupID )
            references Groups( ID ),
        constraint FK_PrimaryAccount foreign key( AccountID )
            references Accounts( ID )
    );
    
    现在您已经正确地进行了1:n基数设计,并且能够为每个组指定一个且仅一个帐户作为主帐户

    然而,有一个缺陷。PrimaryAccounts表必须引用一个现有组和一个现有帐户,但没有任何内容强制要求该帐户与该组关联

    幸运的是,这很容易解决。只需向Accounts表添加一个约束:

        constraint UQ_AccountGroup unique( GroupID, ID ),
    
    然后,您不需要在PrimaryAccounts表中创建两个FK,而只需要一个:

        constraint FK_PrimaryGroupAccount foreign key( GroupID, AccountID )
            references Accounts( GroupID, ID )
    

    现在每个组只能有一个主帐户,并且该帐户必须与该组关联。

    太好了,谢谢!使用过滤后的唯一索引是我多年前使用的一个技巧,但不知何故,当我看到这个案例时,我从未想到过。第一种方法应该可以很好地工作,尽管第二种方法也是一种有趣的方法,我必须将其归档。
        constraint FK_PrimaryGroupAccount foreign key( GroupID, AccountID )
            references Accounts( GroupID, ID )