如何使SQL多对多类型的关系表

时间:2013-09-04 00:06:05

标签: sql

我是SQL的新手,我正在首先尝试尽可能多地学习,因为我正在编码,这很难,因为我正在设计数据库,我将不得不忍受所以我想确保我做得对。我学习了多对多桥接表的基础知识,但是如果这两个字段是相同的类型呢?让我们说一个拥有数千名用户的社交网络,你会如何创建一个表来跟踪谁是谁的朋友?如果有关于每个关系的其他数据怎么办,比如......“约会对象”。知道会有“在dateY和dateZ之间显示所有userX朋友”之类的查询。我的数据库有几种这样的情况,我无法找到一种有效的方法。既然它对我来说很重要,我认为其他人已经找到了设计表格的最佳方法,对吗?

4 个答案:

答案 0 :(得分:33)

创建一个User表,然后创建一个Relationships表,您可以在其中存储两个朋友的id以及有关他们之间关系的任何信息。

SQL图

SQL diagram

MySQL代码

CREATE TABLE `Users` (
  `id` TINYINT NOT NULL AUTO_INCREMENT DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `Relationships` (
  `id` TINYINT NOT NULL AUTO_INCREMENT DEFAULT NULL,
  `userid` TINYINT NULL DEFAULT NULL,
  `friendid` TINYINT NULL DEFAULT NULL,
  `friended` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
);

ALTER TABLE `Relationships` ADD FOREIGN KEY (userid) REFERENCES `Users` (`id`);
ALTER TABLE `Relationships` ADD FOREIGN KEY (friendid) REFERENCES `Users` (`id`);

SQL选择

使用数据填充表后,您可以创建SQL SELECT查询以获取所有朋友。你的朋友是那些id在一边而你的身份在另一边的人。您检查id的双方,这样您就不需要存储两次关系。此外,您必须排除id,因为您不能成为自己的朋友(在正常,健康的世界中)。

SELECT *
FROM Users u
   INNER JOIN Relationships r ON u.id = r.userid
   INNER JOIN Relationships r ON u.id = r.friendid
WHERE
   (r.userid = $myid OR r.friendid = $myid)
   AND r.friended >= $startdate
   AND r.friended <= $enddate
   AND u.id != $myid;

$myid$startdate$enddate可以是PHP变量,因此在双引号中,您可以将此代码直接传递给数据库。

答案 1 :(得分:4)

单个链接表就足够了,如下所示:

People( PersonId bigint, Name nvarchar, etc )
Friends( FromPersonId bigint, ToPersonId bigint, DateAdded datetime )

示例查询:

谁是我的朋友? (即将我加为好友,但未必回复的人)

SELECT
    People.Name
FROM
    Friends
    INNER JOIN People ON Friends.FromPersonId = People.PersonId
WHERE
    Friends.ToPersonId = @myPersonId

是谁在两个日期之间添加了我?

SELECT
    People.Name
FROM
    Friends
    INNER JOIN People ON Friends.FromPersonId = People.PersonId
WHERE
    Friends.ToPersonId = @myPersonId
    AND
    Friends.DateAdded >= @startDate
    AND
    Friends.DateAdded <= @endDate

答案 2 :(得分:4)

模型

可以选择延伸三张桌子的模型。您将拥有明显的表user,其中包含所有用户的详细信息(姓名,出生日期......)。

CREATE TABLE `user` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45),
  `dob` DATE,
  PRIMARY KEY (`id`)
);

其次,用户的表connection可以包含授予连接的权限(此连接可以查看我的相册),更重要的是,它将引用表friendship。我们之间需要表connection,因为一个用户可以连接成许多友谊。

CREATE TABLE `connection` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL,
  `friendship_id` INT NOT NULL,
  `privilege_mask` TINYINT,
  PRIMARY KEY (`id`)
);

friendship反过来可以包括共享细节,例如建立这种友谊时。 连接到同一 friendship 的用户

CREATE TABLE `friendship` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `met_first_time` DATE,
  PRIMARY KEY (`id`)
);

这种方式比目前发布的其他解决方案更加真实,因为它避免了方向性(无法交流的友谊 - 这应该不存在!),但这会更多一些工作实施。

查询好友

应该查询朋友姓名的示例可能是(虽然未经过测试):

SELECT B.name FROM user A
  INNER JOIN connection conn_A ON conn_A.user_id = A.id
    INNER JOIN connection conn_B ON conn_A.friendship_id = conn_B.friendship_id
    INNER JOIN user B ON conn_B.user_id = B.id
    INNER JOIN friendship ON friendship.id = conn_A.friendship_id
  WHERE A.name = 'Dan' AND A.id <> B.id AND
    friendship.met_first_time BETWEEN '2013-4-1' AND '2013-6-30';

您可能会注意到,如果您不关心结交朋友的日期,则无需JOINfriendship表,因为连接已共享friendship_id个键。任何此类查询的实质都是JOINconn_Aconn_B之间的conn_A.friendship_id = conn_B.friendship_id

答案 3 :(得分:2)

您应该在关系表中使用两个用户的ID作为PRIMARY KEY(即使不是双向的,该关系也是唯一的)。像这样的东西

  

CREATE TABLE Usersid int(9)NOT NULL AUTO_INCREMENT,
  PRIMARY KEY(id));

     

CREATE TABLE Relationshipsid1 int(9)NOT NULL,id2 int(9)   NOT NULL,friended TIMESTAMP NOT NULL,PRIMARY KEY(id1,   id2));

请注意:

  • id1和id2引用Users表(上面的代码非常简化)
  • 即使这种关系不是&#34;双向&#34;你能想到 如果你有id1 - id2,似乎id1用户将id2用户添加为 朋友但不一定相反 - 那么id2用户可以添加 id1用户作为朋友 - 在您可以拥有的关系表中 这些可能的组合:
    • id1-id2,(只有1加2为好友)
    • id2-id1,(仅限2加1作为朋友)
    • id1-id2 AND id2-id1(两者 - 表示关系表中有2行,两者都是MUTUAL 朋友)