最初に目を引くのは、のインデックス設定ですfriends
。
あなたは現在これを持っています:
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
key `friendid` (`friendid`)
相互の友情をクロスチェックする場合、friendid
インデックスをトラバースするときにユーザーIDがテーブルから取得される可能性があるため、少し費用がかかる可能性があります。おそらく、次のように索引付けできます。
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
unique key `friendid` (`friendid`,`userid`)
これにより、テーブルにアクセスしてインデックスのみを検索する必要がなくなる可能性があります。
現在、クエリに関しては、新しい一意のインデックスを使用することで両方とも改善される可能性があります。一意のインデックスを作成するとも挿入する必要がなくなり(A,B)
および(B,A)
ので、テーブルへ(A,B)
と(B,A)
、とにかくインデックスだろうが。したがって、2番目のクエリは、別の人が友情を開始したため、誰かが他の人の友達であるかどうかを確認するためにテーブルを調べる必要はありません。このようにして、友情が1人だけで破られた場合、一方的な孤立した友情はありません(最近の生活によく似ているようですね)。
最初のクエリは、一意のインデックスの方がメリットがあるようです。何百万もの行がある場合でも、インデックスのみを使用して友達を見つけると、テーブルに触れないようになります。それでも、UNIONクエリを提示しなかったので、UNIONクエリをお勧めします。
SET @givenuserid = ?;
SELECT B.name "Friend's Name"
FROM
(
SELECT userid FROM friends WHERE friendid=@givenuserid
UNION
SELECT friendid FROM friends WHERE userid=@givenuserid
) A INNER JOIN user B USING (userid);
これにより、各ユーザーIDの友達を確認できます
すべての友情を表示するには、次のコマンドを実行します。
SELECT A.userid,A.name,B.friendid,C.name
FROM user A
INNER JOIN friends B ON A.userid=B.userid
INNER JOIN user C on B.friendid=C.userid;
まず、ここにいくつかのサンプルデータがあります:
mysql> drop database if exists key_ilyuk;
Query OK, 2 rows affected (0.01 sec)
mysql> create database key_ilyuk;
Query OK, 1 row affected (0.00 sec)
mysql> use key_ilyuk
Database changed
mysql> create table user
-> (
-> userid INT NOT NULL AUTO_INCREMENT,
-> name varchar(20),
-> primary key(userid)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.04 sec)
mysql> insert into user (name) values
-> ('rolando'),('pamela'),('dominique'),('carlik'),('diamond');
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> create table friends
-> (
-> userid INT NOT NULL,
-> friendid INT NOT NULL,
-> primary key (userid,friendid),
-> unique key (friendid,userid)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.03 sec)
mysql> insert into friends values (1,2),(2,5),(1,3);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from user;
+--------+-----------+
| userid | name |
+--------+-----------+
| 1 | rolando |
| 2 | pamela |
| 3 | dominique |
| 4 | carlik |
| 5 | diamond |
+--------+-----------+
5 rows in set (0.00 sec)
mysql> select * from friends;
+--------+----------+
| userid | friendid |
+--------+----------+
| 1 | 2 |
| 1 | 3 |
| 2 | 5 |
+--------+----------+
3 rows in set (0.00 sec)
mysql>
すべての関係を見てみましょう
mysql> SELECT A.userid,A.name,B.friendid,C.name
-> FROM user A
-> INNER JOIN friends B ON A.userid=B.userid
-> INNER JOIN user C on B.friendid=C.userid
-> ;
+--------+---------+----------+-----------+
| userid | name | friendid | name |
+--------+---------+----------+-----------+
| 1 | rolando | 2 | pamela |
| 1 | rolando | 3 | dominique |
| 2 | pamela | 5 | diamond |
+--------+---------+----------+-----------+
3 rows in set (0.00 sec)
mysql>
5つのユーザーIDをすべて見て、関係が正しく表示されているかどうかを確認しましょう
mysql> SET @givenuserid = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT B.name "Friend's Name"
-> FROM
-> (
-> SELECT userid FROM friends WHERE friendid=@givenuserid
-> UNION
-> SELECT friendid FROM friends WHERE userid=@givenuserid
-> ) A INNER JOIN user B USING (userid);
+---------------+
| Friend's Name |
+---------------+
| pamela |
| dominique |
+---------------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 2;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT B.name "Friend's Name"
-> FROM
-> (
-> SELECT userid FROM friends WHERE friendid=@givenuserid
-> UNION
-> SELECT friendid FROM friends WHERE userid=@givenuserid
-> ) A INNER JOIN user B USING (userid);
+---------------+
| Friend's Name |
+---------------+
| rolando |
| diamond |
+---------------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 3;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT B.name "Friend's Name"
-> FROM
-> (
-> SELECT userid FROM friends WHERE friendid=@givenuserid
-> UNION
-> SELECT friendid FROM friends WHERE userid=@givenuserid
-> ) A INNER JOIN user B USING (userid);
+---------------+
| Friend's Name |
+---------------+
| rolando |
+---------------+
1 row in set (0.01 sec)
mysql> SET @givenuserid = 4;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT B.name "Friend's Name"
-> FROM
-> (
-> SELECT userid FROM friends WHERE friendid=@givenuserid
-> UNION
-> SELECT friendid FROM friends WHERE userid=@givenuserid
-> ) A INNER JOIN user B USING (userid);
Empty set (0.00 sec)
mysql> SET @givenuserid = 5;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT B.name "Friend's Name"
-> FROM
-> (
-> SELECT userid FROM friends WHERE friendid=@givenuserid
-> UNION
-> SELECT friendid FROM friends WHERE userid=@givenuserid
-> ) A INNER JOIN user B USING (userid);
+---------------+
| Friend's Name |
+---------------+
| pamela |
+---------------+
1 row in set (0.00 sec)
mysql>
それらはすべて私には正しいように見えます。
次に、2番目のクエリを使用して、一致するかどうかを確認します...
mysql> SET @givenuserid = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+-----------+----------+
| name | friendid |
+-----------+----------+
| pamela | 2 |
| dominique | 3 |
+-----------+----------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 2;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+---------+----------+
| name | friendid |
+---------+----------+
| diamond | 5 |
+---------+----------+
1 row in set (0.00 sec)
mysql> SET @givenuserid = 3;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
Empty set (0.00 sec)
mysql> SET @givenuserid = 4;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
Empty set (0.00 sec)
mysql> SET @givenuserid = 5;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
Empty set (0.00 sec)
mysql>
なぜ一致しないのですか?これは、(B,A)
forをロードしなかったため(A,B)
です。(B,A)
リレーションシップを読み込んで、2番目のクエリをもう一度試してみましょう。
mysql> insert into friends values (2,1),(5,2),(3,1);
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SET @givenuserid = 1;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+-----------+----------+
| name | friendid |
+-----------+----------+
| pamela | 2 |
| dominique | 3 |
+-----------+----------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 2;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+---------+----------+
| name | friendid |
+---------+----------+
| rolando | 1 |
| diamond | 5 |
+---------+----------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 3;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+---------+----------+
| name | friendid |
+---------+----------+
| rolando | 1 |
+---------+----------+
1 row in set (0.00 sec)
mysql> SET @givenuserid = 4;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
Empty set (0.00 sec)
mysql> SET @givenuserid = 5;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid
-> FROM friends f inner join user u ON ( u.userid = f.friendid )
-> WHERE f.userid = @givenuserid;
+--------+----------+
| name | friendid |
+--------+----------+
| pamela | 2 |
+--------+----------+
1 row in set (0.00 sec)
mysql>
彼らはまだ一致しません。これは、2番目のクエリが片側のみをチェックしているためです。
(B、A)ではなく(A、B)のみを含むすべての値に対して最初のクエリをチェックしてみましょう。
mysql> SET @givenuserid = 1;
SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
FROM friends f
inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
-> FROM friends f
-> inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
-> WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
+-----------+--------+----------+
| name | userid | friendid |
+-----------+--------+----------+
| pamela | 2 | 2 |
| dominique | 3 | 3 |
+-----------+--------+----------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 2;
FROM friends f
inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
-> FROM friends f
-> inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
-> WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
+---------+--------+----------+
| name | userid | friendid |
+---------+--------+----------+
| rolando | 2 | 1 |
| diamond | 5 | 5 |
+---------+--------+----------+
2 rows in set (0.00 sec)
mysql> SET @givenuserid = 3;
SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
FROM friends f
inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
-> FROM friends f
-> inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
-> WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
+---------+--------+----------+
| name | userid | friendid |
+---------+--------+----------+
| rolando | 3 | 1 |
+---------+--------+----------+
1 row in set (0.00 sec)
mysql> SET @givenuserid = 4;
FROM friends f
inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
-> FROM friends f
-> inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
-> WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
Empty set (0.01 sec)
mysql> SET @givenuserid = 5;
FROM friends f
Query OK, 0 rows affected (0.00 sec)
inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
mysql> SELECT u.name, f.friendid userid, IF(f.userid = @givenuserid, f.friendid, f.userid) friendid
-> FROM friends f
-> inner join user u ON ( u.userid = IF(f.userid = @givenuserid, f.friendid, f.userid))
-> WHERE ( f.userid = @givenuserid or f.friendid = @givenuserid );
+--------+--------+----------+
| name | userid | friendid |
+--------+--------+----------+
| pamela | 5 | 2 |
+--------+--------+----------+
1 row in set (0.00 sec)
mysql>
あなたの最初はうまくいきます。先に述べたように、一意のインデックスの恩恵を受けていると確信していますが、私見UNIONの方が簡単だと思います。一意のインデックスを使用すると、実行と出力の点で6分の1と他の6分の1のように見えます。
あなたは私の提案UNIONに対して最初のクエリをベンチマークし、参照する必要があります。
これはあなたが今日尋ねた良い質問でした。質問に+1してください。