Das erste, was mir auffällt, ist das Index-Setup für friends
.
Sie haben dies im Moment:
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
key `friendid` (`friendid`)
Bei der Überprüfung auf gegenseitige Freundschaft kann dies zu geringen Kosten führen, da die Benutzer-ID beim Durchlaufen des friendid
Index möglicherweise aus der Tabelle abgerufen wird . Vielleicht könnten Sie wie folgt indizieren:
friends
-------
userid
friendid
primary key (`userid`,`friendid`),
unique key `friendid` (`friendid`,`userid`)
Dadurch entfällt möglicherweise die Notwendigkeit, auf die Tabelle zuzugreifen und nur den Index zu durchsuchen.
In Bezug auf die Abfragen können sich beide mit dem neuen eindeutigen Index verbessern. Den eindeutigen Index Erstellen entfällt auch die Notwendigkeit einzufügen (A,B)
und (B,A)
in der Tabelle da (A,B)
und (B,A)
den Index ohnehin wäre. Somit müsste die zweite Abfrage nicht durch die Tabelle blättern, um zu sehen, ob jemand ein Freund von jemand anderem ist, weil eine andere Person die Freundschaft initiiert hat. Auf diese Weise gibt es, wenn die Freundschaft nur von einer Person gebrochen wird, keine verwaisten Freundschaften, die einseitig sind (scheint heutzutage dem Leben sehr ähnlich zu sein, nicht wahr?)
Ihre erste Abfrage scheint mehr vom eindeutigen Index zu profitieren. Selbst bei Millionen von Zeilen würde das Auffinden von Freunden, die nur die Indizes verwenden, das Berühren der Tabelle vermeiden. Da Sie jedoch keine UNION-Abfrage vorgelegt haben, möchte ich eine UNION-Abfrage empfehlen:
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);
Auf diese Weise können Sie sehen, wer die Freunde jeder Benutzer-ID sind
Führen Sie Folgendes aus, um alle Freundschaften anzuzeigen:
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;
Hier sind zunächst einige Beispieldaten:
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>
Schauen wir uns alle Beziehungen an
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>
Schauen wir uns alle 5 Benutzer-IDs an und sehen, ob die Beziehungen korrekt angezeigt werden
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>
Sie sehen für mich alle richtig aus.
Lassen Sie uns nun Ihre zweite Abfrage verwenden, um zu sehen, ob sie übereinstimmt ...
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>
Warum nicht zusammenpassen? Das liegt daran, dass ich das nicht (B,A)
für jeden geladen habe (A,B)
. Lassen Sie mich die (B,A)
Beziehungen laden und Ihre zweite Abfrage erneut versuchen.
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>
Sie passen immer noch nicht zusammen. Das liegt daran, dass Ihre zweite Abfrage nur eine Seite überprüft.
Lassen Sie uns Ihre erste Abfrage mit jedem Wert nur mit (A, B) und nicht mit (B, A) vergleichen:
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>
Dein erstes funktioniert gut. Ich bin sicher, dass es von dem einzigartigen Index profitiert, wie ich bereits sagte, aber meiner Meinung nach ist die UNION einfacher. Mit einem eindeutigen Index scheint es in Bezug auf Ausführung und Ausgabe sechs von anderthalb Dutzend der anderen zu sein.
Sie müssten Ihre erste Anfrage mit meinem Vorschlag UNION vergleichen und sehen.
Dies war eine gute Frage, die Sie heute gestellt haben. +1 für deine Frage.