A
是B
的朋友,那么我应该同时存储值AB
和BA
还是一个值足够?两种方法的优缺点是什么? 这是我的观察结果:
如果我保留两者,则在收到朋友的请求时必须更新两者。
如果我不保留两者,那么当我必须对此表进行多个
JOIN
时,我发现很难。 当前,我保持一种关系。
在这种情况下我该怎么办?有什么建议吗?
#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值。
评论
您是否致力于平台,或者这是一个理论问题?混合方法又如何:分别在单独的表中建模返回的友谊和未返回的友谊,请确保将友谊恰好插入到这些表中的一个中,这对于使用当今的SQL产品是不好的
@onedaywhen-是的,听起来更适合图形数据库。
@NickChammas:这不是理论问题。我正在使用存储在Amazon云中的mysql。
@Chan-啊,这意味着您不能使用检查约束来强制建立关系,而存储方式只有一种(MySQL不会强制执行这些约束)