【问题标题】:Many-to-Many with "Primary"多对多与“主要”
【发布时间】:2008-10-01 22:28:49
【问题描述】:

我正在开发一个需要代表计算机及其用户的数据库。每台计算机可以有多个用户,每个用户可以与多台计算机相关联,所以这是一个经典的多对多关系。但是,还需要有“主要”用户的概念。我必须能够加入反对主要用户以列出所有计算机及其主要用户。我不确定在数据库中构建它的最佳方式是什么:

1) 正如我目前所做的那样:将表与布尔 IsPrimary 列链接起来。加入需要类似 ON (c.computer_id = l.computer_id AND l.is_primary = 1)。它有效,但感觉不对,因为将数据限制为每台计算机只有一个主要用户并不容易。

2) 计算机表上直接指向用户行的字段,用户表中的所有行都代表非主用户。这更好地表示了每台计算机一个主的约束,但使获取计算机用户列表变得更加困难。

3) 计算机表上的字段链接到链接表中的一行。感觉怪怪的……

4) 别的?

描述这种关系的“关系”方式是什么?

编辑: @Mark Brackett:第三个选项对我来说似乎不那么奇怪了,因为您已经展示了它的外观。出于某种原因,我什至没有想过使用复合外键,所以我想我必须在链接表上添加一个标识列才能使其工作。看起来很棒,谢谢!

@j04t:很酷,我很高兴我们现在就 #3 达成一致。

【问题讨论】:

  • 为什么很难在第 2 点列出计算机用户?
  • 因为链接表不包含给定计算机的所有用户。

标签: sql-server database-design


【解决方案1】:

选项 3,虽然感觉很奇怪,但最接近您想要建模的内容。你会做这样的事情:

User { 
   UserId 
   PRIMARY KEY (UserId) 
}

Computer { 
   ComputerId, PrimaryUserId
   PRIMARY KEY (UserId) 
   FOREIGN KEY (ComputerId, PrimaryUserId) 
      REFERENCES Computer_User (ComputerId, UserId) 
}

Computer_User { 
   ComputerId, UserId 
   PRIMARY KEY (ComputerId, UserId)
   FOREIGN KEY (ComputerId) 
      REFERENCES Computer (ComputerId)
   FOREIGN KEY (UserId) 
      REFERENCES User (UserId)
}

这为您提供 0 或 1 个主要用户(如果您愿意,PrimaryUserId 可以为空),必须在 Computer_User 中。编辑:如果用户只能是 1 台计算机的主要用户,则 Computer.PrimaryUserId 上的 UNIQUE CONSTRAINT 将强制执行该操作。请注意,并不要求所有用户都是某台计算机上的主要用户(这将是 1:1 的关系,并要求他们在同一个表中)。

编辑:一些查询向您展示此设计的简单性

--All users of a computer
SELECT User.* 
FROM User 
JOIN Computer_User ON 
   User.UserId = Computer_User.UserId 
WHERE 
   Computer_User.ComputerId = @computerId

--Primary user of a computer
SELECT User.* 
FROM User 
JOIN Computer ON 
   User.UserId = Computer.PrimaryUserId
WHERE 
   Computer.ComputerId = @computerId

--All computers a user has access to
SELECT Computer.*
FROM Computer
JOIN Computer_User ON
   Computer.ComputerId = Computer_User.ComputerId
WHERE
   Computer_User.UserId = @userId

--Primary computer for a user
SELECT Computer.*
FROM Computer
WHERE
    PrimaryUserId = @userId

【讨论】:

  • FK 计算机 -> CompUser 强制计算机的主要用户确实是该计算机的用户之一。严格是好的:)
  • 感谢您的帖子。它让我以一种最初看​​起来很陌生但实际上运作良好的方式看待这种关系。
  • 我喜欢它!从理论上讲,我们在这里说的是,除了称为“是计算机用户”。有人看到此提案有任何限制吗?
  • 我看到的问题是数据不一致的可能性。 PrimaryUserID 字段中指示的用户 ID 可能不在该计算机记录的 Computer_User 链接表中。然后怎样呢?谁是正确的?
  • 我同意马克的观点。主用户可能确实不是用户,所以如果你想要所有用户,你必须同时包括主用户和其他计算机用户,并记住要区分,这样主用户就不会出现两次,如果他既是用户,又是主要用户。
【解决方案2】:

编辑—— 前3次我都没有好好思考... 我投票给—— (第 3 种解决方案)

用户

user id (pk)

电脑

computer id (pk)
primary user id (fk -> computer users id)

计算机用户

user id (pk) (fk -> user id)
computer id (pk) (fk -> user id)

这是我能想到的最佳解决方案。

为什么我喜欢这个设计。

1) 由于这是一种涉及计算机和用户的关系,我喜欢能够将一个用户作为主要用户关联到多台计算机的想法。但是,在使用此数据库的地方可能永远不会发生这种情况。

2) 我不喜欢在链接表上有primary_user的原因

 (computer_users.primary_user_id fk-> users.user_id)

是为了防止一台计算机拥有多个主要用户。

鉴于这些原因,第 3 种解决方案看起来更好,因为您永远不会遇到我在其他方法中看到的一些可能的问题。

解决方案 1 问题 - 每台计算机可能有多个主要用户。

解决方案 2 问题 - 当计算机和用户未相互链接时,计算机链接到主用户。

computer.primaryUser = user.user_id
computer_users.user_id != user.user_id

解决方案 3 问题 - 看起来确实有点奇怪,不是吗?除此之外我什么都想不出来。

解决方案 4 问题 - 我想不出任何其他方法。


这是第 4 次编辑,所以我希望它仍然有意义。

【讨论】:

  • 这仍然没有解决确定“主要”用户的问题。
【解决方案3】:

由于主要用户是计算机和用户的功能,因此我倾向于采用您的方法,即让 primaryUser 成为链接表上的一列。

我能想到的另一种选择是直接在计算机表本身上设置一个 primaryUser 列。

【讨论】:

    【解决方案4】:

    我会创建另一个表 PRIMARY_USERS,在 computer_id 上是唯一的,并同时创建 computer_iduser_id 的 USERS 外键。

    【讨论】:

      【解决方案5】:

      解决方案 1 或 2 都可以。在这一点上,我会问自己哪个更容易使用。我在不同的情况下都使用了这两种方法,尽管我通常会在链接表上使用一个标志,然后对 computer_id 和 isPrimaryUser 强制一个唯一约束,这样可以确保每台计算机只有一个主要用户。

      【讨论】:

      • 除了具有重复computer_id、isPrimaryUser值的非主要用户的明显缺陷......
      • 只需实现第二个唯一键,以便 user_id 和 isPrimaryUser 也必须是唯一的。把家里的所有东西都放在一张桌子上。
      • IsPrimaryUser 可以是 0 或 1。因此,您的 (UserId, IsPrimary) 唯一键将有一个用户最多 1 个非主要用户 (UserId, IsPrimary = 0 ) 和 1 个主要用户 (UserId, IsPrimary = 1)。您的 (ComputerId, IsPrimary) 有类似的缺陷。
      • 发帖后,我意识到(UserId,IsPrimary)键的缺陷。感谢您的帖子,尽管它显示了更好的方法。
      【解决方案6】:

      2 对我来说是正确的,但我会测试 1、2 和 3 在您通常执行的查询种类和您拥有的数据量种类上的性能。

      作为一般经验法则,我倾向于认为,在可以选择实现的地方,您应该查看您的查询要求并设计您的架构,以便在最常见的情况下获得最佳性能和资源利用率。

      在极少数情况下,您有同样常见的情况建议相反的实现,然后使用奥卡姆剃刀。

      【讨论】:

      • 我不同意您在关系建模方面有很多选择。 正确的方式,也有错误的方式。错误的方式可能会表现得更好——但这将是一个维护问题。有时,我们必须做出权衡,但这应该是一个有意识的选择。
      • 只是跟进 - 选项 2 错误,因为它允许用户既是主要用户(通过在 Computer.PrimaryUserId 中)又是非主要用户(同时处于同一台计算机的 Computer_User 中。
      【解决方案7】:

      在我处理的应用程序中,我们有类似的情况,我们的帐户可以附加许多客户,但只有一个应该是主要客户。

      我们使用链接表(如您所用),但在链接表上有一个序列值。主要用户是 Sequence = 1 的用户。然后,我们在该 Link 表上为 AccountID 和 Sequence 设置了一个索引,以确保 AccountID 和 Sequence 的组合是唯一的(从而确保没有两个客户可以是帐户上的主要帐户)。所以你会:

      LEFT JOIN c.computer_id = l.computer_id AND l.sequence = 1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-11
        • 2015-08-19
        • 1970-01-01
        • 1970-01-01
        • 2023-02-14
        • 2018-08-10
        相关资源
        最近更新 更多