LIMIT borne le nombre de lignes renvoyées
LIMIT est le réglage le plus basique en SQL : il indique à SQLite « renvoie-moi au maximum ce nombre de lignes ». Place-le à la fin d'un SELECT et vous récupérerez au plus ce nombre de résultats — pas plus, et parfois moins si la table ne contient pas assez de lignes pour atteindre la limite.
Vous récupérez les trois premières lignes. Mais lesquelles exactement ? C'est là que ça coince : sans ORDER BY, SQLite te renvoie les lignes dans l'ordre qui l'arrange sur le moment. Aujourd'hui, ça peut être l'ordre d'insertion ; demain, après une mise à jour ou la création d'un index, plus rien ne le garantit. Utiliser LIMIT tout seul, ça suffit pour jeter un œil à un échantillon, mais dès que l'ordre compte, il faut le préciser noir sur blanc.
OFFSET : sauter les premières lignes en SQLite
En combinant LIMIT et OFFSET, vous pouvez récupérer une tranche au milieu du résultat. OFFSET k ignore les k premières lignes, puis LIMIT n renvoie au maximum n lignes parmi celles qui restent.
Autrement dit : « saute deux lignes, renvoie les deux suivantes » — les lignes 3 et 4 du résultat trié. Pour bien le visualiser : WHERE filtre, ORDER BY trie, OFFSET saute, LIMIT plafonne. Elles s'exécutent dans cet ordre, et chacune a son importance.
Pagination SQLite : toujours avec ORDER BY
L'usage le plus courant de LIMIT et OFFSET, c'est la pagination — découper une longue liste en pages de 20 lignes par exemple. La page 1, c'est LIMIT 20 OFFSET 0, la page 2 c'est LIMIT 20 OFFSET 20, et ainsi de suite.
Deux choses à retenir. D'abord, le ORDER BY n'est pas optionnel — sans lui, la « page 2 » n'a aucun sens précis et les lignes peuvent changer de place d'un chargement à l'autre. Ensuite, la clé de tri inclut id comme critère de départage. Si deux articles partagent la même valeur de created_at, il vous faut une colonne unique pour fixer leur ordre, sinon leurs positions peuvent s'inverser et une ligne risque de glisser d'une page à l'autre.
Règle à retenir : triez sur quelque chose d'unique, ou ajoutez une colonne unique comme critère de départage à votre colonne de tri.
Une syntaxe raccourcie : LIMIT n, m
SQLite accepte une ancienne syntaxe avec virgule, héritée de la compatibilité avec MySQL : LIMIT offset, count. Elle est équivalente à LIMIT count OFFSET offset, mais l'ordre des arguments est inversé et porte facilement à confusion.
-- Ces deux requêtes sont équivalentes :
SELECT * FROM books LIMIT 10 OFFSET 20;
SELECT * FROM books LIMIT 20, 10; -- d'abord le décalage, puis le nombre
La seconde forme est plus compacte, mais elle piège ceux qui s'attendent à ce que le premier nombre soit la quantité de lignes. Restez sur LIMIT n OFFSET k : c'est explicite et ça se lit naturellement.
OFFSET sans LIMIT en SQLite : l'astuce LIMIT -1
OFFSET ne peut pas s'utiliser seul — la grammaire de SQLite exige qu'il suive un LIMIT. Comment faire alors pour dire « saute les 10 premières lignes et donne-moi tout le reste » ? La convention, c'est LIMIT -1, que SQLite interprète comme « aucune borne supérieure ».
N'importe quelle valeur négative de LIMIT produit le même effet, mais -1 reste l'idiome consacré. On le croise surtout dans des scripts qui parcourent un résultat page par page et qui ont besoin d'une requête « donne-moi tout le reste » pour le dernier lot.
Le piège de performance d'OFFSET
Voici un détail dont personne ne parle avant qu'on se prenne le mur : OFFSET ne dit pas à SQLite de zapper du travail, juste de zapper de la sortie. Pour renvoyer les lignes 10 001 à 10 020, le moteur parcourt quand même les dix mille premières lignes en interne avant de commencer à produire des résultats. Les petits offsets ne coûtent rien ; ceux qui se comptent en dizaines ou centaines de milliers deviennent franchement lents.
Pour la pagination en profondeur, la parade classique s'appelle la pagination par clé (keyset pagination) : au lieu de « saute N lignes », on retient la clé de tri de la dernière ligne lue et on demande « les lignes situées après celle-ci ».
Chaque page effectue une recherche indexée au lieu de parcourir tout ce qui précède. Le compromis : impossible de sauter directement à la « page 47 » — on ne peut qu'avancer dans les données. Pour les flux à défilement infini et les curseurs d'API, c'est exactement le comportement recherché.
La pagination basée sur OFFSET reste pertinente pour les tables d'administration et les petits jeux de résultats. Dès qu'un volume peut grossir sans limite, mieux vaut basculer sur la pagination par keyset.
Exemple complet
On rassemble tout — une requête SQLite paginée avec filtrage, tri et un critère de départage déterministe :
On filtre sur les produits de bureau, on trie par prix croissant avec le nom comme critère de départage, puis on garde les deux premiers. Pour passer à la page 2, il suffit de remplacer OFFSET 0 par OFFSET 2. La requête tient en quelques lignes, mais chaque clause joue un rôle précis.
La suite : DISTINCT
LIMIT définit combien de lignes reviennent ; DISTINCT, lui, détermine si les doublons reviennent ou non. C'est la prochaine clause à ajouter à votre boîte à outils SELECT, et elle est trompeusement simple — on s'en occupe sur la page suivante.
Questions fréquentes
À quoi sert LIMIT en SQLite ?
LIMIT n plafonne le nombre de lignes renvoyées par un SELECT à n au maximum. La clause s'applique après WHERE, GROUP BY et ORDER BY : on limite donc le résultat final, pas les lignes parcourues par le moteur. Par exemple, SELECT * FROM users LIMIT 10 renvoie au plus dix lignes.
Comment OFFSET fonctionne-t-il avec LIMIT en SQLite ?
OFFSET k saute les k premières lignes du résultat avant que LIMIT commence à compter. Concrètement, LIMIT 10 OFFSET 20 renvoie les lignes 21 à 30. Attention : SQLite doit quand même parcourir les lignes ignorées en interne, ce qui explique pourquoi les gros offsets deviennent vite lents.
Peut-on utiliser OFFSET sans LIMIT en SQLite ?
Pas directement : OFFSET n'est valable qu'à l'intérieur d'une clause LIMIT. L'astuce, c'est LIMIT -1 OFFSET k, où -1 signifie « pas de borne supérieure ». SQLite saute alors k lignes et renvoie tout le reste. Une bizarrerie bonne à connaître.
Pourquoi faut-il toujours un ORDER BY pour paginer ?
Sans ORDER BY, SQLite est libre de renvoyer les lignes dans l'ordre qu'il veut, et cet ordre peut changer d'une requête à l'autre. Résultat : la pagination casse — une même ligne peut apparaître en page 1 puis en page 3, ou disparaître complètement. Associez toujours LIMIT/OFFSET à un ORDER BY sur une colonne stable et unique.