多对多与“小学”

时间:2008-10-01 22:28:50

标签: sql-server database-design

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

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

2)计算机表上直接指向用户行的字段,用户表中的所有行表示非主要用户。这表示每个计算机的一次主要约束更好,但却使计算机用户列表变得更难。

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

4)还有别的吗?

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

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

@ j04t:很酷,我很高兴我们现在同意#3。

7 个答案:

答案 0 :(得分:7)

选项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

答案 1 :(得分:1)

编辑 - 我通过前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次编辑,所以我希望它仍然有意义。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

我会在computer_id上制作另一个表格PRIMARY_USERS,并且同时制作USERS的computer_iduser_id个外键。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

2对我来说是正确的,但我会测试1,2和3的性能,通常用于您通常执行的各种查询以及您拥有的各种数据量。

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

在罕见的情况下,你有同样常见的情况建议相反的实施,然后使用奥卡姆的剃刀。

答案 6 :(得分:0)

我们在应用程序中遇到类似情况我们的帐户可以有多个客户连接,但只有一个应该是主要客户。

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

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