我应该如何设计友谊关系表?

Cha*_*han 35 mysql relational-theory

如果A是 的朋友B,那么我应该存储两个值ABBA,还是一个就足够了?两种方法的优缺点是什么。

这是我的观察:

  • 如果我同时保留两个,那么在收到朋友的请求时我必须更新两个。
  • 如果我不保留两者,那么我发现在必须对JOIN这张表进行多次处理时会很困难。

目前,我以一种方式保持这种关系。

在此处输入图片说明

那么在这种情况下我应该怎么做呢?有什么建议吗?

dat*_*god 32

我会存储 AB 和 BA。友谊实际上是一种双向关系,每个实体都与另一个实体相连。尽管直觉上我们认为“友谊”是两个人之间的一个纽带,但从关系的角度来看,它更像是“A 有一个朋友 B”和“B 有一个朋友 A”。两个关系,两个记录。

  • 非常感谢。我真的需要仔细考虑你的想法!我之所以避免存储AB和BA是因为存储,因为每次有朋友时,我的表都会存储两倍。 (3认同)
  • 另外,在只有 AB 而没有 BA 的情况下,这可以代表“待处理的好友请求”。 (2认同)

Mar*_*ith 14

如果友谊是对称的(即不可能A 成为朋友B但反之亦然),那么我将只存储带有检查约束的单向关系,确保每个关系只能以一种方式表示。

此外,我会放弃代理 ID 并使用复合 PK(并且可能在反向列上也有复合唯一索引)。

CREATE TABLE Friends
  (
     UserID1 INT NOT NULL REFERENCES Users(UserID),
     UserID2 INT NOT NULL REFERENCES Users(UserID),
     CONSTRAINT CheckOneWay CHECK (UserID1 < UserID2),
     CONSTRAINT PK_Friends_UserID1_UserID2 PRIMARY KEY (UserID1, UserID2),
     CONSTRAINT UQ_Friends_UserID2_UserID1 UNIQUE (UserID2, UserID1)
  ) 
Run Code Online (Sandbox Code Playgroud)

您不会说这使查询变得困难,但您始终可以创建一个视图

CREATE VIEW Foo
AS
SELECT UserID1,UserID2 
FROM Friends
UNION ALL
SELECT UserID2,UserID1 
FROM Friends
Run Code Online (Sandbox Code Playgroud)


db2*_*db2 7

假设“友谊”总是双向/相互的,我可能会像这样处理它。

CREATE TABLE person (
    person_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns...
)

CREATE TABLE friendship (
    friendship_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns, if any...
)

CREATE TABLE person_friendship (
    person_id int NOT NULL,
    friendship_id int NOT NULL
    PRIMARY KEY (person_id, friendship_id)
)
Run Code Online (Sandbox Code Playgroud)

结果是你把它从多对多连接从“人”变成了“人”,变成了从“人”到“友谊”的多对多连接。这将简化连接和约束,但有一个副作用,即允许两个以上的人建立一个“友谊”(尽管额外的灵活性可能是一个潜在的优势)。


Rol*_*DBA 5

您可能需要围绕友谊定义索引,而不是将行数加倍:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE friendship
(
    friend_of INT NOT NULL,
    friend_to INT NOT NULL,
    PRIMARY KEY (friend_of,friend_to),
    UNIQUE KEY friend_to (friend_to,friend_of)
);
Run Code Online (Sandbox Code Playgroud)

这样,您可以将索引的存储量加倍,而不是表数据的存储量。因此,这应该可以节省 25% 的磁盘空间。MySQL 查询优化器将选择仅执行索引范围扫描,这就是覆盖索引的概念在这里很有效的原因。

以下是有关覆盖索引的一些不错的链接:

警告

如果友谊不是相互的,你就有了另一种关系的基础:追随者

如果friend_to 不是friend_of 的朋友,您可以简单地将该关系排除在表之外。

如果您想为所有类型定义关系,无论它们是否相互关联,您可能可以使用以下表格布局:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE relationship
(
    rel_id INT NOT NULL AUTO_INCREMENT,
    person_id1 INT NOT NULL,
    person_id2 INT NOT NULL,
    reltype_id TINYINT,
    PRIMARY KEY (rel_id),
    UNIQUE KEY outer_affinity (reltype_id,person_id1,person_id2),
    UNIQUE KEY inner_affinity (reltype_id,person_id2,person_id1),
    KEY has_relationship_to (person1_id,reltype_id),
    KEY has_relationship_by (person2_id,reltype_id)
);
CREATE TABLE relation
(
    reltype_id TINYINT NOT NULL AUTO_INCREMENT,
    rel_name VARCHAR(20),
    PRIMARY KEY (reltype_id),
    UNIQUE KEY (rel_name)
);
INSERT INTO relation (relation_name) VALUES
('friend'),('follower'),('foe'),
('forgotabout'),('forsaken'),('fixed');
Run Code Online (Sandbox Code Playgroud)

从关系表中,您可以安排关系以包括以下内容:

  • 朋友应该是相互的
  • 敌人可能是相互的,也可能不是
  • 追随者可能是相互的,也可能不是
  • 其他关系将受到解释(被遗忘或被遗弃或复仇的接受者(固定))
  • 可能的关系可以进一步扩展

这对于所有关系都应该更加稳健,无论这种关系是相互的还是非相互的。