Mysql
 sql >> Base de données >  >> RDS >> Mysql

Quelle est la manière canonique d'extraire un enregistrement d'une base de données MySQL qui a un champ plus petit/plus grand ?

Cette méthode n'est pas rare non plus :

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

Le LEFT JOIN fonctionne sur la base que lorsque s1.rank est à sa valeur maximale, il n'y a pas de s2.rank avec une valeur supérieure et les valeurs des lignes s2 seront NULL.

Mais je dirais que votre façon de faire est la plus courante et la plus facile à comprendre, oui.

EDIT :Sur la question de savoir pourquoi c'est parfois plus lent :

La performance de cette requête dépend de "la prudence avec laquelle elle est écrite". Prenons vos données comme exemple :

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

L'explication de votre requête ressemble à ceci :

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

Celui de ma requête comme ceci :

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

Presque la même. Aucune des requêtes n'utilise d'index, toutes les lignes sont analysées. Nous ajoutons maintenant un index sur la colonne rank .

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

L'explication de votre requête :

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

contre le mien :

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

Votre requête utilise l'index, pas la mienne.

Nous ajoutons maintenant une clé primaire à la table.

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

Expliquez votre requête :

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

et du mien :

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

De cette façon, ils sont probablement aussi rapides. Et c'est ainsi que la requête et la table sont généralement construites. Chaque table doit avoir une clé primaire et si vous exécutez très souvent une requête de filtrage sur la colonne de classement, vous devez bien sûr avoir un index dessus. Il n'y a donc presque aucune différence. Tout dépend maintenant du nombre de lignes que vous avez dans votre table, s'il s'agit d'un index unique et/ou d'un cluster. Mais cela conduirait maintenant un peu trop loin. Mais notez que dans cet exemple, il y a une différence dans le nombre de lignes examinées. Avec de petites données, il n'y a pas de différence, avec de gros volumes de données, il y en a certainement. Mais (!) Ce comportement peut changer pour les deux requêtes, en fonction de l'index.

Et si celui qui écrit la requête fait une erreur ? Et s'il l'écrivait comme ceci :

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

La requête fonctionne toujours et est valide, mais

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

encore une fois l'indice n'est pas utilisé.

Et si nous supprimions à nouveau la clé primaire et écrivions la requête comme ceci :

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 |

L'index est à nouveau utilisé.

Conclusion : Les deux requêtes doivent s'exécuter aussi rapidement, si elles sont bien faites. La vôtre est rapide tant qu'un index est sur la colonne de classement. Il en va de même pour le mien s'il est écrit avec des index à l'esprit.

J'espère que cela vous aidera.