Hal pertama yang menarik perhatian saya adalah pengaturan indeks untuk friends
.
Anda memiliki ini saat ini:
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
key `friendid` (`friendid`)
Saat memeriksa ulang untuk persahabatan bersama, hal itu dapat menimbulkan sedikit biaya karena userid dapat diambil dari tabel saat melintasi friendid
indeks. Mungkin Anda bisa mengindeks sebagai berikut:
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
unique key `friendid` (`friendid`,`userid`)
Ini dapat menghapus kebutuhan untuk mengakses tabel dan mencari indeks saja.
Sekarang, dalam hal kueri, keduanya dapat meningkat dengan indeks unik baru. Membuat indeks unik juga menghilangkan kebutuhan untuk memasukkan (A,B)
dan (B,A)
ke dalam tabel karena (A,B)
dan (B,A)
akan tetap menjadi indeks. Dengan demikian, kueri kedua tidak harus melalui tabel untuk melihat apakah seseorang adalah teman orang lain karena orang lain yang memulai persahabatan. Dengan begitu, jika pertemanan dipatahkan oleh satu orang saja, tidak ada persahabatan yatim yang satu sisi (sepertinya sangat mirip dengan kehidupan akhir-akhir ini, bukan?)
Permintaan pertama Anda sepertinya akan mendapat manfaat lebih dari indeks unik. Bahkan dengan jutaan baris, mencari teman menggunakan indeks saja tidak akan menyentuh meja. Namun, karena Anda tidak menyajikan kueri UNION, saya ingin merekomendasikan kueri 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);
Ini akan membuat Anda melihat siapa teman dari setiap userid
Untuk melihat semua pertemanan, jalankan ini:
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;
Pertama, ini beberapa data sampel:
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>
Mari kita lihat semua hubungannya
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>
Mari kita lihat ke-5 userid dan lihat apakah hubungannya ditampilkan dengan benar
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>
Mereka semua terlihat benar bagiku.
Sekarang, mari gunakan permintaan kedua Anda untuk melihat apakah itu cocok ...
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>
Kenapa tidak cocok? Itu karena saya tidak memuat (B,A)
untuk setiap (A,B)
. Biarkan saya memuat (B,A)
hubungan dan coba permintaan kedua Anda lagi.
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>
Mereka masih belum cocok. Itu karena permintaan kedua Anda hanya memeriksa satu sisi.
Mari kita periksa permintaan pertama Anda terhadap setiap nilai hanya dengan (A, B) dan tidak (B, A):
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>
Pertama Anda bekerja dengan baik. Saya yakin itu mendapat manfaat dari indeks unik seperti yang saya katakan sebelumnya, tapi IMHO saya pikir UNION lebih sederhana. Dengan indeks unik, akan tampak enam dari satu setengah lusin lainnya dalam hal eksekusi dan output.
Anda harus membandingkan permintaan pertama Anda dengan UNION saran saya dan lihat.
Ini adalah pertanyaan bagus yang Anda tanyakan hari ini. +1 untuk pertanyaan Anda.