1

我有一个对称用户关系表:

CREATE TABLE IF NOT EXISTS `friends` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_a` int(11) NOT NULL DEFAULT '0',
  `user_b` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

此表包含以下信息:

  • ID为1的用户是ID为2的用户的好友
  • ID为3的用户是ID为1的用户的好友

得出结论:

  • ID 为 1 的用户有 2 个朋友(ID 3 和 ID 2)
  • 用户 ID 可以在 2 列中的任何一列中(参见用户 ID 1)

如何进行有效查询以检查用户 1 是否是用户 3 的朋友?

为什么我要问一种有效的方法?好吧,因为我有 3 种不同的解决方案(可能还有更多),但我正在努力选择最有效的解决方案。有什么帮助吗?

方法一:

SELECT user_b AS user_a
FROM    friends
WHERE   (user_a = :user_a AND user_b = :user_b)
UNION ALL
SELECT  user_a
FROM    friends
WHERE   (user_b = :user_b AND user_a = :user_a)

方法二:

SELECT * FROM friends WHERE (user_a = :user_a AND user_b = :user_b) OR
(user_b = :user_a AND user_a = :user_b)

方法3:

SELECT user_a FROM (
SELECT user_b AS user_a
FROM    friends
WHERE   user_a = :user_a
UNION ALL
SELECT  user_a
FROM    friends
WHERE   user_b = :user_a
) AS newtab WHERE newtab.user_a = :user_b;

PHP检查:

$my_id = 1;
$friend_id = 3;
$stmt = $dbh->prepare("SELECT ..."); // approach 1 or 2 or 3 or ...
$stmt->bindParam(':user_a', $my_id, PDO::PARAM_STR);
$stmt->bindParam(':user_b', $friend_id, PDO::PARAM_STR);
$stmt->execute();

if ($stmt->rowCount() > 0) {
echo "You are friends";}
else { echo "he is not your friend";}

性能方面 - 哪种方法更好?

编辑:

测试

$start_2 = microtime(true);
for ($i = 1; $i <= 100; $i++) {
    $stmt->execute();
}
$end_2 = microtime(true);

结果

1:0.14095306396484

2:0.063449859619141

3:0.18946194648743

4

2 回答 2

1

您必须为 usersID 创建一个索引,并检查您是否真的需要额外的 ID。

为了简单起见,我会使用选项 2,不确定它是否是最有效的方法,你可以测试女巫一个是否足够简单。

曾经我不得不做类似的事情,其中​​选择查询比插入更常见,并且有很多记录,所以我所做的总是以特定顺序插入,在这种情况下可能类似于 user_a < user_b ,因此您只能检查查询中的一侧。

于 2013-03-04T18:47:41.000 回答
1

正如您已经通过测试发现的那样,方法 2 会更快。

我对此的看法是,在 50% 的情况下,第一部分足以满足该WHERE部分,而第二部分根本不会执行。

添加来自@Luis 的想法,其中您始终拥有 user_a < user_b 那些 50% 将上升到 100%。

此外,连接和子查询可能需要临时表,有时它们甚至必须在磁盘上。这真的很慢,所以应该避免。

要测试查询是否使用临时表运行EXPLAIN并在额外部分中查找使用临时表。

我还将摆脱那个额外的 id(无用数据)并将 user_a、user_b 作为主键。这将为您提供一个快速的索引(只要您知道 user_a < user_b)。

于 2013-03-04T19:01:56.203 回答