代码之家  ›  专栏  ›  技术社区  ›  Neil Williams

多对多与“初级”

  •  6
  • Neil Williams  · 技术社区  · 16 年前

    我正在处理一个需要表示计算机及其用户的数据库。每台计算机可以有多个用户,每个用户可以与多台计算机关联,因此这是一种典型的多对多关系。但是,还需要有一个“主要”用户的概念。我必须能够和主要用户一起列出所有计算机及其主要用户。我不知道在数据库中构造这个的最佳方式是什么:

    1)正如我目前所做的:将表与布尔isprimary列链接。加入需要类似于on(c.computer_id=l.computer_id和l.is_primary=1)。它可以工作,但感觉不对,因为要将数据限制为每台计算机只有一个主要用户并不容易。

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

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

    4)还有别的吗?

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

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

    @J04T:太好了,很高兴我们现在达成一致意见。

    7 回复  |  直到 16 年前
        1
  •  7
  •   Rajeshwaran S P    16 年前

    选项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中。编辑:如果一个用户只能是一台计算机的主用户,那么对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
  •  1
  •   Simurr    16 年前

    编辑—— 我前三次都没好好考虑过… 我投票赞成—— (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)我不喜欢将主要用户放在链接表上的原因

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


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

        3
  •  0
  •   Sijin    16 年前

    因为主用户是计算机和用户的函数,所以我倾向于采用您的方法,让主用户成为链接表上的一列。

    我能想到的另一种选择是将primaryuser列直接放在计算机表本身上。

        4
  •  0
  •   Swati Markus    16 年前

    我会让另一个表的主要用户 computer_id 并使两者兼而有之 计算机识别 user_id 用户的外键。

        5
  •  0
  •   Noah Goodrich    16 年前

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

        6
  •  0
  •   Simon    16 年前

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

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

    在极少数情况下,您会遇到同样常见的情况,建议使用相反的实现,然后使用Occam的Razor。

        7
  •  0
  •   Mark    16 年前

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

    我们使用一个链接表(如您所用),但在链接表上有一个序列值。主要用户是sequence=1的用户。然后, 我们在该链接表上有一个accountid和sequence的索引,以确保accountid的组合 并且序列是唯一的(从而确保一个帐户上没有两个客户是第一个客户)。所以你应该:

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