Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 编写此好友之友sql查询的更有效方法_Mysql - Fatal编程技术网

Mysql 编写此好友之友sql查询的更有效方法

Mysql 编写此好友之友sql查询的更有效方法,mysql,Mysql,我写了这个查询,它返回一个用户的朋友和这些朋友的朋友。因为这里有很多子查询,所以我认为应该有一种更有效的方法来编写它,但这有点超出了我的理解范围 用户表 ++++++++++++++++++ + user_id + name + ++++++++++++++++++ + 1 + bill + + 2 + bob + + 3 + sam + + 4 + ben + ++++++++++++++++++ 用户友谊表 ++++++++++++++

我写了这个查询,它返回一个用户的朋友和这些朋友的朋友。因为这里有很多子查询,所以我认为应该有一种更有效的方法来编写它,但这有点超出了我的理解范围

用户表

++++++++++++++++++
+ user_id + name +
++++++++++++++++++
+ 1       + bill +
+ 2       + bob  +
+ 3       + sam  +
+ 4       + ben  +
++++++++++++++++++
用户友谊表

+++++++++++++++++++++++++++++++++++++
+ sender_user_id + receiver_user_id +
+++++++++++++++++++++++++++++++++++++
+ 1              + 2                +
+ 2              + 3                +
+ 4              + 2                +
+++++++++++++++++++++++++++++++++++++
该表是双向的,因此用户1是用户2的朋友,用户2是用户1的朋友

用户1只有一个朋友用户2。用户2有两个朋友,用户3和用户4

当为用户1运行以下查询时,将返回用户2、3和4

质疑


为了澄清friends和friends of friends查询的最终结果,应该是一个用户ID列表,这样它就可以与用户的表连接以检索名称等。这可能很有帮助:它尝试尽可能少地遍历表

-- 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>

这不会得到名称,只是匹配的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;

也就是说:用户的友谊为您提供了朋友,选择为您提供了朋友的朋友,以确保我们不会将某人算作他们自己的朋友。

下面是一个查询,它提供了所有信息

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>
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>
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>
现在将名称连接到用户表,如下所示:

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 * 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>
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>
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>
试试看

警告

使用左连接而不是内部连接来连接名称,保留了数字返回的顺序

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 * 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>
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>
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>

谢谢你的回复。这看起来更像我正在寻找的,但是我收到了第2行的语法错误,假设这与if语句和mysql有关?显然,我已经用用户id替换了您的用户id。第2行和相应的第2行中有一个点而不是逗号。对不起,我经常遇到这种情况。你说得对,谢谢。如果我使用用户id 1,它只返回用户id 2。它还应该返回3和4,因为它们是2的朋友。不确定第二代的哪个部分不正确好的,第三次是charme:Fixed thinko in IF in second query,现在它可以处理您的数据。非常感谢您的回复。您的解决方案很好,但我最初担心的是,我的整个查询由9条语句组成,因此我希望对其进行优化。我认为这个查询会更慢?我想在所有子查询中使用数字1进行重构会更快谢谢你的回复,这不是我想要的,尽管你会看到,与上面的帖子相比,如果我们添加UF1.sender\u user\u id=1,在WHERE子句之后,这以什么方式不会产生你最初要求的结果?要获得所需修订的单列,只需从选择部分删除UF1项。感谢您的更新。对于原始数据,这在技术上适用于您的修订,但是关系是定向的。通过只搜索发送者\用户\ id=1,我们忽略了其他人(即接收者\用户\ id=1)发起的任何友谊。我已经更改了friendships表中的数据以反映这种情况,在这种情况下,只返回用户2和3。