Je veux comprendre ce qui suit.
Supposons que j'ai une requête compliquée avec disons une jointure de 5 tables par groupe par sommations et par ordre.
Laisser de côté toute optimisation de la requête elle-même, par exemple index, etc.
Y a-t-il un avantage significatif en termes de performances en utilisant LIMIT
? Je suppose que toutes les requêtes (et les résultats) doivent être traités avant LIMIT est appliqué, donc l'utilisation d'un LIMIT pour récupérer un sous-ensemble des résultats, cela offre-t-il une amélioration significative/perceptible?
Si vous souhaitez profiter de LIMIT
pour améliorer les performances, vous devez
LIMIT
avant JOIN
Ces principes peuvent aller très loin si vous pouvez les orchestrer.
J'ai appris ces concepts en en regardant cette vidéo YouTube (écoutez attentivement l'accent français)
J'ai utilisé ces concepts pour répondre à une question StackOverflow très difficile sur l'obtention des 40 meilleurs articles de certaines tables: 12 mai 2011: Récupération d'une seule ligne à partir de la table de jointure .
Dans ma réponse à cette question (16 mai 2011) , j'ai écrit la requête suivante et l'ai testée à fond:
SELECT
AAA.author_id,
AAA.date_created,
IFNULL(BBB.title,'<NO_TITLE>') title,
IFNULL(CCC.filename,'<NO-IMAGE>') filename,
IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
SELECT
AA.id,
AA.date_added,
BB.author_id,
BB.date_created
FROM
(
SELECT
A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
LEFT JOIN article_images B ON A.id = B.article_id
GROUP BY A.id
) AA
INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;
Veuillez noter la ligne dans la requête avec le LIMIT
FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
Cette sous-requête est enfouie à trois niveaux de profondeur. Cela m'a permis d'obtenir les 40 derniers articles en utilisant LIMIT
. Ensuite, j'ai effectué les JOIN nécessaires par la suite.
LIMIT
à l'intérieur des sous-requêtes peut ne pas toujours être la réponse en raison de la cardinalité des index, du contenu des données et de la taille du jeu de résultats à partir de LIMIT
. Si vous avez tous vos "canards d'affilée" (ayez les quatre principes à l'esprit pour votre requête), vous pouvez obtenir des résultats étonnamment bons.LIMIT
en rassemblant uniquement les clés.Lorsqu'une requête est exécutée, elle est d'abord traduite en un plan composé de plusieurs opérateurs. Il existe deux types d'opérateurs de base: bloquant et non bloquant. Un opérateur non bloquant récupère une ligne (ou quelques lignes) de son ou ses enfants pour chaque ligne demandée. Un opérateur de blocage, d'autre part, doit lire et traiter l'ensemble de lignes de tous ses enfants avant de pouvoir produire une sortie.
Le tri est un opérateur de blocage typique. Ainsi, une sélection avec commande par ne bénéficie pas beaucoup d'une limite. Cependant, il existe des SGBDR qui peuvent utiliser un algorithme de tri qui nécessite moins de mémoire et est plus rapide lorsqu'une clause limite est fournie. Dans ce cas, il suffit de stocker les n premières lignes actuellement et de les retirer de la mémoire au fur et à mesure que les lignes précédentes se présentent. Cela peut être un gain de performances significatif. Cependant, je ne suis pas sûr à 100% que MySQL ait cette capacité.
Quoi qu'il en soit, même un tri par limite doit toujours traiter l'ensemble de lignes d'entrée avant de pouvoir produire la première ligne de sortie. Bien que cet algorithme, s'il est implémenté, puisse accélérer le tri, si le reste de la requête est la partie la plus coûteuse, le temps d'exécution total ne s'améliorera pas de manière significative en raison d'une limite fournie.
Dans mon cas, je peux dire Oui, même si je ne comprends toujours pas pourquoi.
SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;
(result set)
8 rows in set (**18.14 sec**)
Notez le temps: 18 secondes. Même demande avec une grosse LIMITE:
SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;
(exact same result set)
8 rows in set (**1.32 sec**)
Plus de dix fois plus rapide !!!
EXPLAIN donne le même résultat pour les deux requêtes.
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 1 | SIMPLE | a1_ | NULL | ALL | IDX_438010BBC10784EF | NULL | NULL | NULL | 795135 | 33.33 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | g0_ | NULL | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4 | phs.a1_.groupe_jardinerie_id | 1 | 50.00 | Using where |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
LIMIT ne devrait interférer que pour limiter le jeu de résultats (c'est-à-dire que si je fais un LIMIT 4, je n'ai que les 4 premières lignes du jeu de résultats ci-dessus).