Comment MySQL traite-t-il ORDER BY et LIMIT dans une requête?

251

J'ai une requête qui ressemble à ceci:

SELECT article FROM table1 ORDER BY publish_date LIMIT 20

Comment fonctionne ORDER BY? Ordonnera-t-il tous les enregistrements, puis obtiendra-t-il les 20 premiers, ou obtiendra-t-il 20 enregistrements et les publish_dateclassera-t-il sur le terrain?

Si c'est le dernier, vous n'êtes pas assuré d'obtenir vraiment les 20 articles les plus récents.

Alex
la source
6
Notez que si certains publish_dates sont égaux, la commande par eux ne donne pas de résultats déterminés, ce qui signifie que si vous utilisez LIMITpour la pagination, vous pouvez finir par obtenir les mêmes éléments sur différentes pages!
Konrad Morawski

Réponses:

245

Il ordonnera d'abord, puis obtiendra les 20 premiers. Une base de données traitera également tout ce qui se trouve dans la WHEREclause précédente ORDER BY.

James
la source
1
Le timing est donc le même?
Yasar Arafath
7
Faux! LIMITles pauses ORDER BY. Avec LIMITun ORDER BYretour de mauvais résultat. LIMITréorganise en quelque sorte le jeu de résultats retourné parORDER BY
Vert
6
@Vert, tu te trompes. Lisez ceci pour l'explication: dev.mysql.com/doc/refman/5.7/en/limit-optimization.html Lorsque la colonne ORDER BY est indexée, elle peut renvoyer des enregistrements dans un ordre différent que sans LIMIT, lorsqu'il y a plus de 1 enregistrements avec la même valeur dans cette colonne.
yitwail
1
Une solution rapide pour de tels problèmes consiste à ajouter une colonne de plus à la commande en ayant de préférence des valeurs uniques afin que la base de données obtienne une règle cohérente pour la commande des lignes lorsque la valeur de la première commande par colonne est la même pour plusieurs lignes.
rineez
37

La clause LIMIT peut être utilisée pour limiter le nombre de lignes renvoyées par l'instruction SELECT. LIMIT prend un ou deux arguments numériques, qui doivent tous deux être des constantes entières non négatives (sauf lors de l'utilisation d'instructions préparées).

Avec deux arguments, le premier argument spécifie le décalage de la première ligne à renvoyer et le second spécifie le nombre maximal de lignes à renvoyer. Le décalage de la ligne initiale est 0 (pas 1):

SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15

Pour récupérer toutes les lignes d'un certain décalage jusqu'à la fin de l'ensemble de résultats, vous pouvez utiliser un grand nombre pour le deuxième paramètre. Cette instruction récupère toutes les lignes de la 96e ligne à la dernière:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Avec un argument, la valeur spécifie le nombre de lignes à renvoyer depuis le début de l'ensemble de résultats:

SELECT * FROM tbl LIMIT 5; # Retrieve first 5 rows

En d'autres termes, LIMIT row_count est équivalent à LIMIT 0, row_count.

Tous les détails sur: http://dev.mysql.com/doc/refman/5.0/en/select.html

bensiu
la source
N'est-ce pas récupérer les lignes 5-14?
Adonis K. Kakoulidis
@adonis Non, ce n'est pas le cas. L'exemple provient directement de la documentation MySQL
dcaswell
Le numéro 5 est la 6ème rangée. 5 lignes (0 à 4) sont ignorées.
Phil Perry
1
Mais l'utilisation de LIMIT sans ORDER BY peut donner des résultats incohérents! Malheureusement, l'ensemble des résultats doit être commandé avant l'application de la LIMIT ou le SGBD est libre de commander arbitrairement le résultat, puis OFFSET et LIMIT sur cet ensemble. J'ai lu que cela peut être dû au fait que le SGBD sélectionne un autre plan de requête basé sur OFFSET et LIMIT, donc l'ordre arbitraire.
Barton
4
question demande la limite et l'ordre par. Mais la réponse n'est pas du tout liée à cette question
Shen liang
9

Tout comme @James le dit, il ordonnera tous les enregistrements, puis obtiendra les 20 premières lignes.

Dans l'état actuel des choses, vous êtes assuré d'obtenir les 20 premiers articles publiés, les plus récents ne seront pas affichés.

Dans votre situation, je vous recommande d' ajouter descà order by publish_date, si vous voulez que les articles les plus récents, le plus récent article sera le premier.

Si vous avez besoin de conserver le résultat dans l'ordre croissant et que vous ne voulez toujours que les 10 derniers articles, vous pouvez demander à mysql de trier votre résultat deux fois.

Cette requête ci-dessous triera le résultat par ordre décroissant et limitera le résultat à 10 (c'est-à-dire la requête entre parenthèses). Il sera toujours trié par ordre décroissant, et nous ne sommes pas satisfaits de cela, nous demandons donc à mysql de le trier une fois de plus. Nous avons maintenant le résultat le plus récent sur la dernière ligne.

select t.article 
from 
    (select article, publish_date 
     from table1
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

Si vous avez besoin de toutes les colonnes, cela se fait de cette façon:

select t.* 
from 
    (select * 
     from table1  
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

J'utilise cette technique lorsque j'écris manuellement des requêtes pour examiner la base de données pour diverses choses. Je ne l'ai pas utilisé dans un environnement de production, mais maintenant quand je l'ai marqué, le tri supplémentaire n'a pas d'impact sur les performances.

emanciperingsivraren
la source
2
Votre tri supplémentaire ne peut pratiquement pas avoir d'impact mesurable sur les performances car il est limité à 10 lignes / éléments uniquement :-). En général, le tri d'une table en mémoire (qu'une sous-sélection produit) est très rapide et à peine mesurable, sauf si vous avez des millions de lignes ou si le SGBD pagine le jeu de résultats sur le disque car il ne correspond pas à la mémoire (auquel cas en fonction du SGBD, il peut également annuler la requête).
Martin Kersten
7

S'il existe un index approprié, dans ce cas sur le publish_dateterrain, alors MySQL n'a pas besoin d'analyser l'intégralité de l'index pour obtenir les 20 enregistrements demandés - les 20 enregistrements seront trouvés au début de l'index. Mais s'il n'y a pas d'index approprié, une analyse complète de la table sera nécessaire.

Il y a un article MySQL Performance Blog de 2009 à ce sujet.

Martin Clayton
la source
7

Vous pouvez ajouter [asc] ou [desc] à la fin de la commande pour obtenir les enregistrements les plus anciens ou les plus récents

Par exemple, cela vous donnera d'abord les derniers enregistrements

ORDER BY stamp DESC

Ajouter la LIMITclause aprèsORDER BY

Paul
la source
3
Bienvenue sur stackoverflow. Je pense que vous avez peut-être mal compris la question. Je pense qu'ils demandaient l'ordre des opérations plutôt que "comment trier". (Mais c'est théorique puisque la question a déjà été répondue il y a un certain temps;)
Leigh
5

Vous pouvez utiliser ce code SELECT article FROM table1 ORDER BY publish_date LIMIT 0,10 où 0 est une limite de début d'enregistrement et 10 nombre d'enregistrement

gaurangkathiriya
la source
8
Non, ce n'est pas obligatoire . LIMIT 10est un raccourci pour LIMIT 0,10.
Lawrence Dol
2
oui, non requis pour LIMIT 0,10 Mais vous pouvez exiger comme ceci Limit 10,20
gaurangkathiriya
3

LIMIT est généralement appliqué comme dernière opération, donc le résultat sera d'abord trié puis limité à 20. En fait, le tri s'arrêtera dès que les 20 premiers résultats triés seront trouvés.

Egor Pavlikhin
la source
11
Votre deuxième phrase va à l'encontre de la première. Le tri ne peut pas s'arrêter lorsque les 20 premiers résultats sont trouvés car, comme vous l'avez dit, le tri sera effectué avant le retour des résultats. MySQL ne peut connaître les 20 premiers résultats qu'une fois le tri terminé.
Tom
@Tom, en fait, il peut, s'il est commandé par une colonne indexée. C'est expliqué ici: dev.mysql.com/doc/refman/5.7/en/limit-optimization.html
yitwail
-3

La syntaxe de LIMIT est également différente selon les bases de données, par exemple:

mysql - LIMITE 1, 2

postgres - LIMITE 2 OFFSET 1

harish sharma
la source