如果AB的朋友,那么我应该同时存储值ABBA还是一个值足够?两种方法的优缺点是什么?

这是我的观察结果:


如果我保留两者,则在收到朋友的请求时必须更新两者。
如果我不保留两者,那么当我必须对此表进行多个JOIN时,我发现很难。

当前,我保持一种关系。



在这种情况下我该怎么办?有什么建议吗?

评论

您是否致力于平台,或者这是一个理论问题?

混合方法又如何:分别在单独的表中建模返回的友谊和未返回的友谊,请确保将友谊恰好插入到这些表中的一个中,这对于使用当今的SQL产品是不好的
@onedaywhen-是的,听起来更适合图形数据库。

@NickChammas:这不是理论问题。我正在使用存储在Amazon云中的mysql。

@Chan-啊,这意味着您不能使用检查约束来强制建立关系,而存储方式只有一种(MySQL不会强制执行这些约束)

#1 楼

我将存储AB和BA。友谊实际上是一种双向关系,每个实体相互联系。即使从直觉上我们将“友谊”视为两个人之间的一个链接,但从关系的角度来看,它更像是“ A有朋友B”和“ B有朋友A”。两个关系,两个记录。

评论


非常感谢。我真的需要仔细考虑您的想法!我避免存储AB和BA的原因是由于存储的缘故,由于每次建立友谊时,我的表都将存储两倍的存储量。

–陈
2012年1月5日18:31

您对存储的看法是正确的,但是请记住,如果将其存储为整数,则每个亲朋好友关系大约需要30个字节(2个记录x 3列x每个整数4个字节= 24个字节加上一些填充)。 100万个有10个朋友的人仍然只有大约300MB的数据。

– datagod
2012年1月5日19:48



datagod:是的!

–陈
2012年1月6日at 2:32

这也是我设计桌子AB和BA的方式。

–kabuto178
13年2月6日在13:14

另外,在只有AB而没有BA的情况下,这可能表示“待处理的好友请求”。

–格雷格
2014年5月13日13:31

#2 楼

如果友谊是对称的(即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)
  ) 


评论


我知道这已经很老了,非常抱歉。为了不给INSERT带来不必要和多余的额外负担,不定义反向友谊索引UNIQUE会更好吗?由于我们具有主键(a,b),并且PK是唯一的,因此无论如何,反向键(b,a)也是唯​​一的。

–tfrommen
2015年9月17日在16:51



@tf猜猜这取决于查询优化器。正如您所指出的,只需要单向检查,这样插入计划就可以执行此操作。这个问题被标记为MySQL-不知道它的行为。

–马丁·史密斯
2015年9月17日19:21在

我知道这是一个古老的答案,但是我只想指出一个绊脚石,那就是MySQL完全忽略了CHECK约束(尽管它将成功地“解析”它们),所以这种方法可能不是该技术所采用的方法。

–弥迦
16年8月8日在21:17

@Micah是的。我在2012年还没有意识到。仍然可以在其他DBMS中使用...

–马丁·史密斯
16年8月8日在21:22

+1用于实现该视图。存储AB和BA会带来不一致(如果关系不是双向的),而此方法是更好的方法

– imans77
19年3月14日在16:39

#3 楼

假设“友谊”总是双向的,那么我可能会这样处理。

从“人”到“人”的多对多联接,从“人”到“友谊”的多对多联接。这将简化联接和约束,但具有副作用,即允许在一个“友谊”中容纳两个以上的人(尽管可能会有更多的灵活性)。

评论


这基本上是一个组/成员模式。有趣的想法。

–einSelbst
2014年12月6日23:12

#4 楼

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

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)
);


这样,您将索引的存储空间增加了一倍,而表数据的存储空间却增加了一倍。结果,这将节省25%的磁盘空间。 MySQL Query Optimizer将只选择执行索引范围扫描,这就是覆盖索引的概念在这里很好用的原因。
以下是覆盖索引的一些不错的链接:


http://peter-zaitsev.livejournal.com/6949.html
http://www.mysqlperformanceblog.com/2006/11/23/covering-index-and-prefix-indexes/
http://ronaldbradford.com/blog/tag/covering-index/

CAVEAT

如果友谊不是相互的,那么你就有建立另一种关系的基础:FOLLOWER

如果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');


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

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

这应该是无论是相互关系还是相互关系,对所有关系都更强大。

评论


嗨@rolandomysqldba,我非常支持您的答案。它对我真的很有帮助(在本例中为第一个示例)。现在对我来说是一个警告,我想要独特的关系。 (例如,如果用户A与B成为朋友,那么B与A成为朋友是不可接受的。)我应该使用触发器吗?那性能呢?因为我有一个非常大的表(大约一百万条记录),并且如果我搜索用户A的朋友(A存储在两个(friend_of,friend_to)字段中,并且mysql仅使用一个索引,那么它的执行速度很慢。我必须在表中存储重复的条目(例如A-> B,B-> A),还有更好的选择吗?

– Manish足pk
2014年12月13日上午11:57

#5 楼

如果您可以在应用程序中控制A的ID始终小于B的ID(预先订购A,B元素ID),则可以利用无OR的询问功能(选择id_A = a AND ID_B = b而不是询问) (id_A = a AND id_B = b)或(id_A = b AND id_B = a)),并使用另一方的近似值维护所需记录的一半。然后,您应该使用另一个字段来维护关系的状态(are-friends,a-to-to-b,b-soto-to-a,exfriends-a,exfriends-b),然后完成。 br />
这是我管理友谊系统的方式,它简化了系统,并使用了其他系统所需的一半行,只说A等于代码中的较低id值。