Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Was ist der kanonische Weg, um einen Datensatz aus einer MySQL-Datenbank zu ziehen, die ein kleinstes/größtes Feld hat?

Auch dieser Weg ist nicht ungewöhnlich:

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.uid IS NULL;

Der LEFT JOIN funktioniert auf der Grundlage, dass es keinen s2.rank mit einem größeren Wert gibt, wenn s1.rank seinen maximalen Wert hat, und die Werte der s2-Zeilen NULL sind.

Aber ich würde sagen, dass Ihre Art, es zu tun, die gebräuchlichste und am einfachsten zu verstehende Art ist, ja.

EDIT:Auf die Frage warum es manchmal langsamer ist:

Die Leistung dieser Abfrage hängt davon ab, "wie sorgfältig sie geschrieben ist". Nehmen wir Ihre Daten als Beispiel:

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int)
;

INSERT INTO students
    (`uid`, `last_name`, `first_name`, `dob`, `email`, `rank`, `grade`)
VALUES
    (13428700000001, 'Smith', 'John', '1990-12-03', '[email protected]', 99, 4),
    (13428721960000, 'Li', 'Kai Li', '1979-02-15', '[email protected]', 12, 2),
    (13428722180001, 'Zhang', 'Xi Xiong', '1993-11-09', '[email protected]', 5, 5),
    (13428739950000, 'Zhou', 'Ji Hai', '1991-06-06', '[email protected]', 234, 1),
    (13428739950001, 'Pan', 'Yao', '1992-05-12', '[email protected]', 43, 2),
    (13428740010001, 'Jin', 'Denny', '1994-06-02', '[email protected]', 198, 3),
    (13428740010002, 'Li', 'Fonzie', '1991-02-02', '[email protected]', 75, 3),
    (13428743370000, 'Ma', 'Haggar', '1991-08-16', '[email protected]', 47, 4),
    (13428743590001, 'Ren', 'Jenny', '1990-03-29', '[email protected]', 5, 2),
    (13428774040000, 'Chen', 'Dragon', '1999-04-12', '[email protected]', 23, 5),
    (13428774260001, 'Wang', 'Doctor', '1996-09-30', '[email protected]', 1, 5),
    (13430100000000, 'Chanz', 'Heyvery', '1994-04-04', '[email protected]', 107, 2)
;

Die Erläuterung Ihrer Abfrage sieht folgendermaßen aus:

| ID | SELECT_TYPE |    TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
-------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |  ALL |        (null) | (null) |  (null) | (null) |   12 | Using where |
|  2 |    SUBQUERY | students |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |

Die aus meiner Abfrage wie folgt:

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |        (null) | (null) |  (null) | (null) |   12 | Using where |

Fast das gleiche. Keine Abfrage verwendet einen Index, alle Zeilen werden gescannt. Jetzt fügen wir einen Index zur Spalte rank hinzu .

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
    , key rankkey(rank)
    )
;

Die Erklärung aus Ihrer Anfrage:

| ID | SELECT_TYPE |    TABLE |   TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF |   ROWS |                        EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |    ref |       rankkey | rankkey |       5 |  const |      1 |                  Using where |
|  2 |    SUBQUERY |   (null) | (null) |        (null) |  (null) |  (null) | (null) | (null) | Select tables optimized away |

gegen meins:

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |       rankkey | (null) |  (null) | (null) |   12 | Using where |

Ihre Abfrage verwendet den Index, meine nicht.

Jetzt fügen wir der Tabelle einen Primärschlüssel hinzu.

drop table if exists students;
CREATE TABLE students
    (`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
    , key rankkey(rank)
    , primary key(uid)
    );

Erläutern Sie Ihre Anfrage:

| ID | SELECT_TYPE |    TABLE |   TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF |   ROWS |                        EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
|  1 |     PRIMARY | students |    ref |       rankkey | rankkey |       5 |  const |      1 |                  Using where |
|  2 |    SUBQUERY |   (null) | (null) |        (null) |  (null) |  (null) | (null) | (null) | Select tables optimized away |

und von mir:

| ID | SELECT_TYPE | TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF | ROWS |                                EXTRA |
-------------------------------------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |   ALL |        (null) |  (null) |  (null) | (null) |   12 |                                      |
|  1 |      SIMPLE |    s2 | index |       rankkey | rankkey |       5 | (null) |   12 | Using where; Using index; Not exists |

Auf diese Weise sind sie höchstwahrscheinlich gleich schnell. Und so werden normalerweise die Abfrage und die Tabelle aufgebaut. Jede Tabelle sollte einen Primärschlüssel haben, und wenn Sie sehr oft eine Abfrage ausführen, die nach der Rangspalte filtert, sollten Sie natürlich einen Index darauf haben. Also fast kein Unterschied. Es hängt jetzt alles davon ab, wie viele Zeilen Sie in Ihrer Tabelle haben, ob es sich um einen eindeutigen Index und/oder einen geclusterten Index handelt. Aber das würde jetzt etwas zu weit führen. Beachten Sie jedoch, dass in diesem Beispiel ein Unterschied darin besteht, wie viele Zeilen untersucht werden. Bei kleinen Datenmengen gibt es keinen Unterschied, bei großen Datenmengen schon. Aber(!) dieses Verhalten kann sich je nach Index für beide Abfragen ändern.

Was ist, wenn derjenige, der die Abfrage schreibt, einen Fehler macht? Was, wenn er es so schreibt:

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.last_name IS NULL;

Die Abfrage funktioniert immer noch und ist gültig, aber

| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
----------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |  ALL |        (null) | (null) |  (null) | (null) |   12 |             |
|  1 |      SIMPLE |    s2 |  ALL |       rankkey | (null) |  (null) | (null) |   12 | Using where |

Auch hier wird der Index nicht verwendet.

Was ist, wenn wir den Primärschlüssel wieder entfernen und die Abfrage so schreiben:

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.rank IS NULL;

| ID | SELECT_TYPE | TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |    REF | ROWS |                    EXTRA |
-------------------------------------------------------------------------------------------------------------------
|  1 |      SIMPLE |    s1 |   ALL |        (null) |  (null) |  (null) | (null) |   12 |                          |
|  1 |      SIMPLE |    s2 | index |       rankkey | rankkey |       5 | (null) |   12 | Using where; Using index |

Index wird wieder verwendet.

Fazit: Beide Abfragen sollten bei richtiger Ausführung gleich schnell ausgeführt werden. Ihre Abfrage ist schnell, solange sich ein Index in einer Rangspalte befindet. Gleiches gilt für meine, wenn sie mit Indizes geschrieben wurde.

Hoffe das hilft.