2012-01-06 96 views
0

我写过这个查询,它返回一个用户朋友和这些朋友的朋友。由于在这里有很多子查询,我认为有很多的写法是更有效的方式,但它有点超出我的意思。更有效的写这个朋友的朋友sql查询

用户表

++++++++++++++++++ 
+ user_id + name + 
++++++++++++++++++ 
+ 1  + bill + 
+ 2  + bob + 
+ 3  + sam + 
+ 4  + ben + 
++++++++++++++++++ 

user_friendships表

+++++++++++++++++++++++++++++++++++++ 
+ sender_user_id + receiver_user_id + 
+++++++++++++++++++++++++++++++++++++ 
+ 1    + 2    + 
+ 2    + 3    + 
+ 4    + 2    + 
+++++++++++++++++++++++++++++++++++++ 

表是双向的,从而用户1是用户2和用户2的朋友是用户的朋友1.

用户1只有1个好友用户2.用户2有2个好友,用户3和用户4.

当针对用户1运行下面的查询时,返回用户2,3和4。

查询

SELECT * FROM users 
WHERE (user_id IN 

(SELECT receiver_user_id as user_id 
FROM user_friendships 
WHERE sender_user_id IN 

(SELECT receiver_user_id as user_id 
FROM user_friendships 
WHERE sender_user_id = '1' 
UNION 
SELECT sender_user_id as user_id 
FROM user_friendships 
WHERE receiver_user_id = '1') 

UNION 

SELECT sender_user_id as user_id 
FROM user_friendships 
WHERE receiver_user_id IN 

(SELECT receiver_user_id as user_id 
FROM user_friendships 
WHERE sender_user_id = '1' 
UNION 
SELECT sender_user_id as user_id 
FROM user_friendships 
WHERE receiver_user_id = '1') 

) 

OR user_id IN 

(SELECT receiver_user_id as user_id 
FROM user_friendships 
WHERE sender_user_id = '1' 
UNION 
SELECT sender_user_id as user_id 
FROM user_friendships 
WHERE receiver_user_id = '1') 
) 
AND user_id != '1' 

要澄清的朋友和朋友的查询好友的最终结果应该是user_ids的一个列表,以便它可以与用户的表连接,以检索名称等

回答

1

这可能很有帮助:它尽可能少地尝试遍历表。

-- First generation: Friends 
SELECT 
    IF(firstgen.sender_user_id=<your-user-id>,firstgen.receiver_user_id,firstgen.sender_user_id) AS friend 
FROM 
    user_friendships AS firstgen 
WHERE 
    firstgen.receiver_user_id=<your-user-id> 
    OR firstgen.sender_user_id=<your-user-id> 

UNION 

-- Second generation: Friends of friends 
SELECT 
    IF(secondgen.sender_user_id in(firstgen.sender_user_id,firstgen.receiver_user_id),secondgen.receiver_user_id,secondgen.sender_user_id) AS friend 
FROM 
    user_friendships AS firstgen 
    INNER JOIN user_friendships AS secondgen ON 
    (firstgen.sender_user_id=<your-user-id> AND (secondgen.sender_user_id=firstgen.receiver_user_id OR secondgen.receiver_user_id=firstgen.receiver_user_id)) 
    OR 
    (firstgen.receiver_user_id=<your-user-id> AND (secondgen.sender_user_id=firstgen.sender_user_id OR secondgen.receiver_user_id=firstgen.sender_user_id)) 
WHERE 
    firstgen.receiver_user_id=<your-user-id> 
    OR firstgen.sender_user_id=<your-user-id> 
+0

感谢您的回复顺序。这看起来更像我在找什么,但是我收到了第2行的语法错误,假设它与if语句和mysql有关?显然,我已经用用户标识替换了你的用户标识。 – leejmurphy 2012-01-06 22:27:12

+0

第2行有一个点而不是逗号(稍后会有相应的行)对不起,我总是发生在我身上。 – 2012-01-06 22:36:29

+0

你是对的,谢谢。如果我使用用户ID 1,它只返回用户ID 2.它也应该返回3和4,因为他们是2的朋友。不知道第二代的哪部分不正确 – leejmurphy 2012-01-06 22:47:23

0

这不会让你的名字,只是匹配ID:

user_friendships 
UNION 
SELECT UF1.sender_user_id AS sender_user_id, 
     UF2.receiver_user_id AS receiver_user_id 
FROM user_friendships as UF1, 
    user_friendships as UF2 
WHERE UF1.receiver_user_id = UF2.sender_user_id 
    AND UF1.sender_user_id != UF2.receiver_user_id; 

那就是:user_friendships给你的朋友,和SELECT给你的朋友 - 的 - 朋友(使当然,我们不会把某人当作自己的朋友)。

+0

感谢您的回复,虽然你会看到与上面的帖子相比 – leejmurphy 2012-01-06 22:49:57

+1

如果我们在WHERE子句之后添加“UF1.sender_user_id = 1 AND”,那么这不会产生什么最初要求? (要获得请求修订的单列,只需从SELECT部分​​删除UF1项目。) – 2012-01-07 00:20:33

+0

感谢您的更新。原始数据在技术上适用于您的修订,但是这些关系是有方向性的。通过仅搜索sender_user_id = 1,我们忽略了我们由其他人发起的任何友谊,即receiver_user_id = 1。我更改了友谊表中的数据以反映这种情况,在这种情况下,只返回用户2和3。 – leejmurphy 2012-01-07 13:34:07

1

这是导致一切

SELECT * FROM (SELECT * FROM 
(SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
UNION 
(SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
UNION 
SELECT * FROM 
(SELECT uf1.sender_user_id,uf2.receiver_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.receiver_user_id = uf2.sender_user_id) C 
UNION 
SELECT * FROM 
(SELECT uf1.receiver_user_id,uf2.sender_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.sender_user_id = uf2.receiver_user_id) D; 

注意

  • 子查询A和B是第一代双向查询
  • 子查询C和d是第二代双向

以下是您的示例数据:

DROP DATABASE IF EXISTS friends; 
CREATE DATABASE friends; 
USE friends 
CREATE TABLE users 
(id int not null auto_increment, 
name varchar(10),primary key (id)); 
insert into users (name) values 
('bill'),('bob'),('sam'),('ben'); 
CREATE TABLE user_friendships 
(sender_user_id int not null, 
receiver_user_id int not null, 
primary key (sender_user_id,receiver_user_id), 
unique key (receiver_user_id,sender_user_id)); 
insert into user_friendships values 
(1,2),(2,3),(2,4); 

这是你的样本数据加载

mysql> DROP DATABASE IF EXISTS friends; 
(id int not null auto_increment, 
name varchar(10),primary key (id)); 
insert into users (name) values 
('bill'),('bob'),('sam'),('ben'); 
CREATE TABLE user_friendships 
(sender_user_id int not null, 
receiver_user_id int not null, 
primary key (sender_user_id,receiver_user_id), 
unique key (receiver_user_id,sender_user_id)); 
insert into user_friendships values 
(1,2),(2,3),(2,4); 
Query OK, 2 rows affected (0.08 sec) 

mysql> CREATE DATABASE friends; 
Query OK, 1 row affected (0.00 sec) 

mysql> USE friends 
Database changed 
mysql> CREATE TABLE users 
    -> (id int not null auto_increment, 
    -> name varchar(10),primary key (id)); 
Query OK, 0 rows affected (0.08 sec) 

mysql> insert into users (name) values 
    -> ('bill'),('bob'),('sam'),('ben'); 
Query OK, 4 rows affected (0.07 sec) 
Records: 4 Duplicates: 0 Warnings: 0 

mysql> CREATE TABLE user_friendships 
    -> (sender_user_id int not null, 
    -> receiver_user_id int not null, 
    -> primary key (sender_user_id,receiver_user_id), 
    -> unique key (receiver_user_id,sender_user_id)); 
Query OK, 0 rows affected (0.06 sec) 

mysql> insert into user_friendships values 
    -> (1,2),(2,3),(2,4); 
Query OK, 3 rows affected (0.06 sec) 
Records: 3 Duplicates: 0 Warnings: 0 

mysql> select * from users; 
+----+------+ 
| id | name | 
+----+------+ 
| 1 | bill | 
| 2 | bob | 
| 3 | sam | 
| 4 | ben | 
+----+------+ 
4 rows in set (0.00 sec) 

mysql> select * from user_friendships; 
+----------------+------------------+ 
| sender_user_id | receiver_user_id | 
+----------------+------------------+ 
|    1 |    2 | 
|    2 |    3 | 
|    2 |    4 | 
+----------------+------------------+ 
3 rows in set (0.00 sec) 

mysql> 

这里是“一切查询”

mysql> SELECT * FROM (SELECT * FROM 
    -> (SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
    -> UNION 
    -> (SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.sender_user_id,uf2.receiver_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.receiver_user_id = uf2.sender_user_id) C 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.receiver_user_id,uf2.sender_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.sender_user_id = uf2.receiver_user_id) D; 
+----------------+------------------+ 
| sender_user_id | receiver_user_id | 
+----------------+------------------+ 
|    1 |    2 | 
|    2 |    3 | 
|    2 |    4 | 
|    2 |    1 | 
|    3 |    2 | 
|    4 |    2 | 
|    1 |    3 | 
|    1 |    4 | 
|    3 |    1 | 
|    4 |    1 | 
+----------------+------------------+ 
10 rows in set (0.00 sec) 

mysql> 

现在的运行看用户1只是关系,只要使用此查询

SELECT * FROM (
SELECT * FROM (SELECT * FROM 
(SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
UNION 
(SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
UNION 
SELECT * FROM 
(SELECT uf1.sender_user_id,uf2.receiver_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.receiver_user_id = uf2.sender_user_id) C 
UNION 
SELECT * FROM 
(SELECT uf1.receiver_user_id,uf2.sender_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
WHERE (sender_user_id=1 and receiver_user_id<>1) 
or (sender_user_id<>1 and receiver_user_id=1); 

这里是输出

mysql> SELECT * FROM (
    -> SELECT * FROM (SELECT * FROM 
    -> (SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
    -> UNION 
    -> (SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.sender_user_id,uf2.receiver_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.receiver_user_id = uf2.sender_user_id) C 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.receiver_user_id,uf2.sender_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
    -> WHERE (sender_user_id=1 and receiver_user_id<>1) 
    -> or (sender_user_id<>1 and receiver_user_id=1); 
+----------------+------------------+ 
| sender_user_id | receiver_user_id | 
+----------------+------------------+ 
|    1 |    2 | 
|    2 |    1 | 
|    1 |    3 | 
|    1 |    4 | 
|    3 |    1 | 
|    4 |    1 | 
+----------------+------------------+ 
6 rows in set (0.00 sec) 

mysql> 

现在挂钩frmo用户表中的名称是这样的:

SELECT u1.name,u2.name FROM (
SELECT * FROM (SELECT * FROM 
(SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
UNION 
(SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
UNION 
SELECT * FROM 
(SELECT uf1.sender_user_id,uf2.receiver_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.receiver_user_id = uf2.sender_user_id) C 
UNION 
SELECT * FROM 
(SELECT uf1.receiver_user_id,uf2.sender_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
INNER JOIN users u1 ON everything.sender_user_id = u1.id 
INNER JOIN users u2 ON everything.receiver_user_id = u2.id 
WHERE (sender_user_id=1 and receiver_user_id<>1) 
or (sender_user_id<>1 and receiver_user_id=1); 

这里是输出

mysql> SELECT u1.name,u2.name FROM (
    -> SELECT * FROM (SELECT * FROM 
    -> (SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
    -> UNION 
    -> (SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.sender_user_id,uf2.receiver_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.receiver_user_id = uf2.sender_user_id) C 
    -> UNION 
    -> SELECT * FROM 
    -> (SELECT uf1.receiver_user_id,uf2.sender_user_id 
    -> FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    -> ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
    -> INNER JOIN users u1 ON everything.sender_user_id = u1.id 
    -> INNER JOIN users u2 ON everything.receiver_user_id = u2.id 
    -> WHERE (sender_user_id=1 and receiver_user_id<>1) 
    -> or (sender_user_id<>1 and receiver_user_id=1); 
+------+------+ 
| name | name | 
+------+------+ 
| bob | bill | 
| sam | bill | 
| ben | bill | 
| bill | bob | 
| bill | sam | 
| bill | ben | 
+------+------+ 
6 rows in set (0.00 sec) 

mysql> 

试试看!

CAVEAT

胡克使用左边的名字,而不是JOIN内蒙古JOIN保留数被退回

SELECT u1.name,u2.name FROM (
SELECT * FROM (SELECT * FROM 
(SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
UNION 
(SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
UNION 
SELECT * FROM 
(SELECT uf1.sender_user_id,uf2.receiver_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.receiver_user_id = uf2.sender_user_id) C 
UNION 
SELECT * FROM 
(SELECT uf1.receiver_user_id,uf2.sender_user_id 
FROM user_friendships uf1 INNER JOIN user_friendships uf2 
ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
LEFT JOIN users u1 ON everything.sender_user_id = u1.id 
LEFT JOIN users u2 ON everything.receiver_user_id = u2.id 
WHERE (sender_user_id=1 and receiver_user_id<>1) 
or (sender_user_id<>1 and receiver_user_id=1); 

这里是输出

mysql>  SELECT u1.name,u2.name FROM (
    ->  SELECT * FROM (SELECT * FROM 
    ->  (SELECT sender_user_id,receiver_user_id FROM user_friendships) A 
    ->  UNION 
    ->  (SELECT receiver_user_id,sender_user_id FROM user_friendships)) B 
    ->  UNION 
    ->  SELECT * FROM 
    ->  (SELECT uf1.sender_user_id,uf2.receiver_user_id 
    ->  FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    ->  ON uf1.receiver_user_id = uf2.sender_user_id) C 
    ->  UNION 
    ->  SELECT * FROM 
    ->  (SELECT uf1.receiver_user_id,uf2.sender_user_id 
    ->  FROM user_friendships uf1 INNER JOIN user_friendships uf2 
    ->  ON uf1.sender_user_id = uf2.receiver_user_id) D) everything 
    ->  LEFT JOIN users u1 ON everything.sender_user_id = u1.id 
    ->  LEFT JOIN users u2 ON everything.receiver_user_id = u2.id 
    ->  WHERE (sender_user_id=1 and receiver_user_id<>1) 
    ->  or (sender_user_id<>1 and receiver_user_id=1); 
+------+------+ 
| name | name | 
+------+------+ 
| bill | bob | 
| bob | bill | 
| bill | sam | 
| bill | ben | 
| sam | bill | 
| ben | bill | 
+------+------+ 
6 rows in set (0.00 sec) 

mysql> 
+0

非常感谢您的回复。您的解决方案很好,但是我最初的担心是我的整体查询由9条语句组成,所以我期待优化它。我认为这个查询会更慢? – leejmurphy 2012-01-06 22:36:36

+0

我想在所有的子查询中使用数字1进行重构会让它更快 – RolandoMySQLDBA 2012-01-06 22:39:53