MySQL offset lignes infinies


114

Je voudrais construire une requête qui affiche tous les résultats dans une table, mais qui est décalée de 5 à partir du début de la table. Pour autant que je sache, MySQL LIMITnécessite une limite ainsi qu'un décalage. Y a-t-il un moyen de faire ça?


1
C'est une question tout à fait valable, mais je me demande si ce qui serait mieux est de tout saisir et de ne pas tenir compte des premiers enregistrements par programme. Compte tenu de l'horreur de ce qui semble être la meilleure réponse (limite 5, 18446744073709551615), je préférerais fortement contourner les limites de LIMIT de MySQL.
cesoid

3
@cesoid et si vous voulez limit 5000, 18446744073709551615. Vous n'allez pas récupérer 5000 lignes supplémentaires juste pour que votre code soit joli.
elipoultorak

@ user3576887 Je pense que vous avez raison, je considérais juste la question ci-dessus en supposant que 5 était la seule exigence, plutôt qu'une quantité variable qui pourrait être beaucoup plus grande (et plutôt que de résoudre le problème de quelqu'un d'autre).
cesoid

Je suggère que c'est une tâche si rare que la laideur de la solution peut être acceptée.
Rick James

Réponses:


151

À partir du manuel MySQL sur LIMIT :

Pour récupérer toutes les lignes d'un certain décalage jusqu'à la fin du jeu 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;

105
Terrible! Je suis venu ici en espérant que MySQL rende la clause Limit facultative, telle quelle, mais aussi avec un offset fourni ... mais non! J'ai vu cette dispersion de 18446744073709551615 dans tout le code et je blâmais les programmeurs paresseux, mais c'est une caractéristique de conception!
Petruza

8
réponse horrible, mais c'est officiel de MySQL Doc. Ce que je peux dire @ _ @
GusDeCooL

21
18446744073709551615 est 2 ^ 64-1 pour ceux qui se demandaient. Vous voudrez peut-être faire attention car vous ne pourrez pas stocker cette valeur dans un entier 32 bits. Vous devez vous assurer de stocker cela sous forme de chaîne pour garantir la compatibilité.
AlicanC

13
Terrible! ils doivent être plus élégants que cela ... Limit -1ou avoir l' Limit Nullair assez raisonnable! ou atleast Limit devrait accepter une sous-requête commeselect * from table limit (select count(*) from table)
vulcan raven

19
utilisez php 'PHP_INT_MAX' pour éviter les effets de débordement.
Karl Adler

24

Comme vous l'avez mentionné, LIMIT est requis, vous devez donc utiliser la plus grande limite possible, qui est 18446744073709551615 (maximum de BIGINT non signé)

SELECT * FROM somewhere LIMIT 18446744073709551610 OFFSET 5

33
Wow, est-ce la solution officielle de l'équipe MySQL?
Antony

12

Comme indiqué dans d'autres réponses, MySQL suggère d'utiliser 18446744073709551615 comme nombre d'enregistrements dans la limite, mais considérez ceci: que feriez-vous si vous récupériez 18 446 744 073 709 551 615 enregistrements? En fait, que feriez-vous si vous aviez 1 000 000 000 d'enregistrements?

Peut-être voulez-vous plus d'un milliard d'enregistrements, mais ce que je veux dire , c'est qu'il y a une limite au nombre que vous voulez , et c'est moins de 18 quintillions. Pour des raisons de stabilité, d'optimisation et éventuellement de convivialité, je suggérerais de mettre une limite significative à la requête. Cela réduirait également la confusion pour quiconque n'a jamais vu ce nombre magique et aurait l'avantage supplémentaire de communiquer au moins combien d'enregistrements vous êtes prêt à traiter à la fois.

Si vous devez vraiment obtenir les 18 quintillions d'enregistrements de votre base de données, peut-être que vous voulez vraiment les récupérer par incréments de 100 millions et effectuer une boucle 184 milliards de fois.


Vous avez raison, mais garder cette décision au développeur n'est pas un bon choix
amd

@amd Pouvez-vous expliquer cela un peu plus? Je ne sais pas ce que vous essayez de dire.
cesoïde

1
@cesoid Je pense qu'il dit que les développeurs ne devraient pas être ceux qui choisissent arbitrairement la logique métier, ce avec quoi je suis d'accord, mais seulement jusqu'à un certain point. Disons que vous renvoyez une liste de commandes à un client. Il est parfaitement raisonnable de ne jamais rendre plus d'un million, disons, à la fois, mais limiter à 100 pourrait prêter à confusion.
Autumn Leonard

@amd Je ne dis pas que le développeur devrait changer le comportement de l'application afin d'éviter d'utiliser 18446744073709551615. Je dis qu'ils devraient se demander si l'utilisation de ce numéro a du sens dans le cadre de la mise en œuvre de tout le client ou le concepteur d'interface a demandé, et qu'il est très peu probable que ce soit la bonne mise en œuvre pour quoi que ce soit. La décision d'utiliser MySQL a probablement déjà été prise par le développeur sans se demander s'il y aurait plus de 18 quintillions de quelque chose.
cesoid

5

Une autre approche consisterait à sélectionner une colonne auto-incrémentée, puis à la filtrer à l'aide de HAVING.

SET @a := 0; 
select @a:=@a + 1 AS counter, table.* FROM table 
HAVING counter > 4

Mais je m'en tiendrai probablement à l'approche de limite supérieure.


merci, et je me demande comment puis-je mettre une telle requête dans une déclaration PHP! Je veux dire comme ça$sql = 'SET @a :=0 SELECT .....';
Reham Fahmy

2

Comme d'autres l'ont mentionné, dans le manuel MySQL. Pour y parvenir, vous pouvez utiliser la valeur maximale d'un gros int non signé, c'est-à-dire ce nombre affreux (18446744073709551615). Mais pour le rendre un peu moins compliqué, vous pouvez utiliser l'opérateur binaire tilde "~".

  LIMIT 95, ~0

cela fonctionne comme une négation au niveau du bit. Le résultat de "~ 0" est 18446744073709551615.


1
Ne fonctionne pas dans MariaDB 10.3 :( J'ai essayé les deux LIMIT 5, ~0et LIMIT ~0 OFFSET 5. Est-ce une fonctionnalité MySQL 8.0?
jurchiks

1
Ce n'est pas une chose dans MySQL 5.7 - syntaxe invalide.
Jonny Nott le

0

Juste aujourd'hui, je lisais sur la meilleure façon d'obtenir d'énormes quantités de données (plus d'un million de lignes) à partir d'une table mysql. Une façon est, comme suggéré, d'utiliser LIMIT x,yxest le décalage et yla dernière ligne que vous souhaitez renvoyer. Cependant, comme je l'ai découvert, ce n'est pas le moyen le plus efficace de le faire. Si vous avez une colonne d'auto-incrémentation, vous pouvez aussi facilement utiliser une SELECTinstruction avec une WHEREclause indiquant à partir de quel enregistrement vous souhaitez démarrer.

Par exemple, SELECT * FROM table_name WHERE id > x;

Il semble que mysql obtient tous les résultats lorsque vous utilisez LIMITet ne vous montre que les enregistrements qui correspondent à l'offset: pas le meilleur pour les performances.

Source: Réponse à cette question Forums MySQL . Prenez juste note, la question est d'environ 6 ans.


13
Cela donnera des résultats incorrects si vous avez déjà supprimé un enregistrement. Cette méthode est particulièrement dangereuse, car elle fonctionne la plupart du temps et échoue silencieusement lorsqu'elle ne le fait pas.
octern

0

Vous pouvez utiliser une instruction MySQL avec LIMIT:

START TRANSACTION;
SET @my_offset = 5;
SET @rows = (SELECT COUNT(*) FROM my_table);
PREPARE statement FROM 'SELECT * FROM my_table LIMIT ? OFFSET ?';
EXECUTE statement USING @rows, @my_offset;
COMMIT;

Testé dans MySQL 5.5.44. Ainsi, nous pouvons éviter l'insertion du numéro 18446744073709551615.

note: la transaction s'assure que la variable @rows est en accord avec la table considérée dans l'exécution de l'instruction.


comme @amd l'a déclaré: "sélectionner le nombre (*) sur une table avec 7 millions d'enregistrements prend environ 17
secondes

-1

Je sais que c'est vieux mais je n'ai pas vu de réponse similaire, c'est donc la solution que j'utiliserais.

Tout d'abord, j'exécuterais une requête de comptage sur la table pour voir combien d'enregistrements existent. Cette requête est rapide et normalement le temps d'exécution est négligeable. Quelque chose comme:

SELECT COUNT(*) FROM table_name;

Ensuite, je créerais ma requête en utilisant le résultat obtenu de count comme limite (puisque c'est le nombre maximum de lignes que la table pourrait éventuellement renvoyer). Quelque chose comme:

SELECT * FROM table_name LIMIT count_result OFFSET desired_offset;

Ou peut-être quelque chose comme:

SELECT * FROM table_name LIMIT desired_offset, count_result;

Bien sûr, si nécessaire, vous pouvez soustraire le paramètre souhaité_offset de count_result pour obtenir une valeur réelle et précise à fournir comme limite. Passer la valeur "18446744073709551610" n'a tout simplement pas de sens si je peux réellement déterminer une limite appropriée à fournir.


2
sélectionner le nombre (*) sur une table avec 7 millions d'enregistrements prend environ 17s
amd

-7
WHERE .... AND id > <YOUROFFSET>

id peut être n'importe quelle colonne numérique auto-incrémentée ou unique que vous avez ...


7
Mauvaise idée. Cela donnera un décalage incorrect si vous avez déjà supprimé une ligne.
octern
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.