从表中选择条件1或反转条件1的有效方法

时间:2013-03-04 18:30:36

标签: php mysql performance

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

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个不同的解决方案(可能还有更多),但是我很勉强选择最有效的解决方案。有什么帮助吗?

方法1:

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)

方法2:

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

2 个答案:

答案 0 :(得分:1)

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

我会使用选项2来简化,不确定它是否是最有效的方法,你可以测试一个更快的简单。

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

答案 1 :(得分:1)

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

我对此的理解是,在50%的情况下,第一部分足以满足WHERE部分,第二部分将不会被执行。

从@Luis添加想法,你总是拥有user_a&lt; user_b那些50%将升至100%。

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

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

我还会删除那个额外的id(无用的数据)并将user_a,user_b作为主键。那会给你一个快速的索引(只要你知道user_a&lt; user_b)。