Sql server “多对多”和“多对多”;“初级”;

Sql server “多对多”和“多对多”;“初级”;,sql-server,database-design,Sql Server,Database Design,我正在开发一个需要表示计算机及其用户的数据库。每台计算机可以有多个用户,并且每个用户可以与多台计算机关联,因此这是一种典型的多对多关系。然而,还需要一个“主要”用户的概念。我必须能够加入对抗主要用户的行列,列出所有拥有其主要用户的计算机。我不确定在数据库中构造此结构的最佳方式: 1) 正如我目前所做的:将表链接到一个boolean IsPrimary列。加入需要类似于ON的内容(c.computer\u id=l.computer\u id和l.is\u primary=1)。它可以工作,但感觉

我正在开发一个需要表示计算机及其用户的数据库。每台计算机可以有多个用户,并且每个用户可以与多台计算机关联,因此这是一种典型的多对多关系。然而,还需要一个“主要”用户的概念。我必须能够加入对抗主要用户的行列,列出所有拥有其主要用户的计算机。我不确定在数据库中构造此结构的最佳方式:

1) 正如我目前所做的:将表链接到一个boolean IsPrimary列。加入需要类似于ON的内容(c.computer\u id=l.computer\u id和l.is\u primary=1)。它可以工作,但感觉不对,因为不容易将数据限制为每台计算机只有一个主用户

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

3) 计算机表上链接到链接表中某行的字段。感觉很奇怪

4) 还有别的吗

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

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


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

因为主要用户是计算机的一个功能,而用户我倾向于使用您的方法,将主要用户作为链接表上的一列


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

我会在
计算机id
上创建另一个表primaryUser,并创建用户的
计算机id
用户id
外键。

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

2让我感觉不错,但我会测试1,2和3,以了解您通常执行的查询种类和数据量种类的性能

作为一般的经验法则,我倾向于相信,在有实现选择的地方,您应该根据查询需求设计模式,以便在最常见的情况下获得最佳性能和资源利用率

在罕见的情况下,如果您有同样常见的情况,建议使用相反的实现,请使用Occam的razor。

编辑-- 前三次我没好好考虑过。。。 我投赞成票-- (3号解决方案)

使用者

计算机

computer id (pk)
primary user id (fk -> computer users id)
计算机用户

user id (pk) (fk -> user id)
computer id (pk) (fk -> user id)
这是我能想到的最好的解决办法

为什么我喜欢这个设计

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

2) 我不喜欢链接表上有主用户的原因

 (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问题-我想不出任何其他方法



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

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

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

LEFT JOIN c.computer_id = l.computer_id AND l.sequence = 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可以为null),该用户必须位于计算机用户中。编辑:如果一个用户只能是一台计算机的主用户,则computer.PrimaryUserId上的唯一约束将强制执行该约束。请注意,不要求所有用户都是某台计算机上的主用户(这将是一个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

为什么很难在第2点列出计算机用户?因为链接表不包含给定计算机的所有用户。除了非主要用户具有重复计算机id的明显缺陷外,ISPrimary用户值……只需实现第二个唯一键,以便用户id和ISPrimary用户也必须是唯一的。将所有内容放在一个表中。ISPrimary用户可以是0或1。因此,您的(UserId,IsPrimary)唯一密钥将使一个用户最多为1个非主用户(UserId,IsPrimary=0)和1个主用户(UserId,IsPrimary=1)。你的(ComputerId,IsPrimary)也有类似的缺陷。发布后,我意识到(UserId,IsPrimary)密钥中存在该缺陷。谢谢你的帖子
--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