2012-01-06 75 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