【问题标题】:Avoid data redundancy in a friends table避免朋友表中的数据冗余
【发布时间】:2015-11-23 04:37:39
【问题描述】:

我正在尝试设计一个非常简单的表格来存储社区中朋友的数据。

因此我分别存储了2个朋友的userId

目标

用户想要加载他/她的朋友列表。

t_friends 选项 1:

查询

SELECT * FROM t_friend WHRE user_id = 10 

10 是正在查找好友列表的用户的当前userId,并且他有1 个好友userId(20)

这样userId (10) 可以找到他的朋友(20) 但是如果userId(20) 正在寻找他的朋友呢? 查询加上userId

这让我想到了另一个包含冗余数据的设计:

t_friends 选项 2:

userId (10) 立即加载:

SELECT * FROM t_friend WHRE user_id=10

类似于userId(20) 的查询将是:

SELECT * FROM t_friend WHRE user_id=20

但是冗余呢?这导致我使用表设计选项 1 进行该查询:

SELECT * FROM t_friend WHERE user_id=10 OR friend_id=10

我觉得有一种更聪明的方法可以解决这个问题。您对这种结构有任何经验吗?

谢谢

【问题讨论】:

  • 冗余怎么样?你所做的非常好,没有更聪明的方法,不管有人会写什么。您查询用户的朋友。您的数据已标准化,一切都很完美。如果您可以通过尝试“优化”来实现任何目标,那么它就会破坏整个事情,创建不必要的复杂查询并使系统难以维护。无论谁或有什么使用 SQL 的经验,当人们看到您创建的表时 - 乍一看,一切都非常合理。 TL;DR:不要改变任何东西。
  • 谢谢@Mjh。但是这样我们就有了两倍的数据。而不是例如10GB 你将有 20GB 扫描等等。
  • @bub 您使用的最终查询非常好,在我看来问题是被过度思考了。每个关系一行,使用 OR 查询,完成。你可以在里面扔一个 IF 来避免它在 php 中:SELECT IF(user_id=10, friend_id, user_id) AS target_id FROM t_friend WHERE user_id=10 OR friend_id=10
  • 您正在解决一个数据过多的问题,方法是实现一个通过“机智”系统获取关系的复杂过程——这是无法完成的。此外,如果您考虑到存储要求 - 10 或 20 GB 真的很小。如果您的列是 4 字节整数,那么在任何压缩开始之前,每条记录花费 12 个字节,等于 833, 333, 333 行。不要通过破坏逻辑和破坏数据库的工作方式来解决存储问题。 @ChrisBaker 还提供了一个出色的解决方案,您可以实施该解决方案来弥补冗余。
  • 另外一件事,特别是如果你关心表的物理数据大小,去掉id字段,让主键是user_id和friend_id的组合。那里不需要一个任意的自增数字,人们不能多次成为朋友,所以主键自然是两个用户 ID 的组合。

标签: php mysql database redundancy


【解决方案1】:

添加字段friendship_key:

ALTER TABLE t_friend ADDfriendship_key 十进制(22,11);

CREATE UNIQUE INDEXfriendly_key_unique ON t_friend (friendship_key);

还有php部分:

$friends = [$userId, $friendId];
$key = min($friends).'.'.max($friends);

$q = "SELECT * FROM t_friend WHERE friendship_key = ".$key;

插入:

$friends = [$userId, $friendId];
$key = min($friends).'.'.max($friends);

$q = "INSERT INTO t_friend (friendship_key, userId, friendId) VALUES (".implode(',', [$key, $userId, $friendId]).")";

我没有使用 VARCHAR 作为友谊键,而是使用十进制来最小化关系键的数据。

为简单起见,只需创建函数:

function insertFriendship($user1, $user2) {
    $key = min([$user1, $user2]).'.'.max([$user1, $user2]);
    $q = "INSERT INTO t_friend (friendship_key, userId, friendId) VALUES (".implode(',', [$key, $user1, $user2]).")";
    mysql_query($q);
}

function getFriendsOf($user) {
    $q = "SELECT * FROM t_friends WHERE ".$user." IN (userId, friendId)";
    return mysql_query($q);
}

function areFriends($user1, $user2) {
    $key = min([$user1, $user2]).'.'.max([$user1, $user2]);
    $q = "SELECT 1 FROM t_friends WHERE friendship_key = ".$key." LIMIT 1";
    $q = mysql_query($q);
    return (mysql_num_rows($q)>0);
}

【讨论】:

  • 感谢您的努力 :) 我必须实施您的解决方案,一旦可行,我将接受。谢谢
【解决方案2】:

我认为这是存储关系数据的唯一方法。当您存储关系时,请尝试将最小值存储为 userId,将最大值存储为friendId。使这两个值完全唯一,您将不会得到任何重复的值。当您搜索用户时,请使用以下内容

SELECT * FROM t_friend WHERE user_id=10 OR friend_id=10

【讨论】:

  • 谢谢。为什么要区分最小值和最大值?
  • 插入值时不想做额外搜索的表单
【解决方案3】:

您可能希望使用以下查询来验证您的用户是否还不是另一个人的朋友:

INSERT INTO t_friend (userId, friendId)
SELECT 10, 20
WHERE NOT EXISTS (  SELECT userId
                    FROM t_friend 
                    WHERE userId = 20
                    AND friendId = 10)

感谢这个(法语)关于冗余验证的话题here

【讨论】:

  • 谢谢。但这无济于事,因为您不知道朋友 ID。但是您的查询假定知道一个 20 的用户 ID。
  • 所以你应该添加另一个关于如何插入值的精度,我猜你在插入行之前知道两个用户的 id,对吧?
  • 是的,我在插入时知道这两个 id。但是另一个精度是什么意思?
  • 您的解决方案容易并发,因此使用起来不安全。在 SQL 世界中,我们不会在插入之前检查某些内容是否存在。如果我们不希望特定列值组合的重复,我们只需插入并放置unique 约束。 MySQL 甚至允许我们使用INSERT IGNOREINSERT INTO ... ON DUPLICATE KEY UPDATE。您链接的网站也不应该被信任,太多没有经验的人不知道提出他们无法测试的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 2019-01-02
  • 2011-01-31
  • 1970-01-01
相关资源
最近更新 更多