SQL : ORDER BY et LIMIT

Trier les résultats, paginer, récupérer les N premiers — syntaxe, tri multi-colonnes, OFFSET et performances

9
Sections
20+
Exemples
SQL
Standard

SECTION 01

ORDER BY — trier les résultats

🔤 Le principe

Sans ORDER BY, SQL ne garantit aucun ordre dans les résultats. La base de données est libre de retourner les lignes dans n’importe quel ordre — souvent celui du stockage physique, mais ce n’est pas fiable. Si tu veux un ordre prévisible, il faut toujours le demander explicitement.

ORDER BY trie le résultat final d’une requête. C’est l’avant-dernière étape d’exécution (juste avant LIMIT). Tu peux trier en ordre croissant (ASC, par défaut) ou décroissant (DESC).

— Tri croissant (A→Z, petit→grand) — ASC est le défaut
SELECT nom, age FROM clients
ORDER BY nom; — équivalent à ORDER BY nom ASC

— Tri décroissant (Z→A, grand→petit)
SELECT nom, age FROM clients
ORDER BY age DESC;

— Trier par date décroissante (les plus récents d’abord)
SELECT * FROM commandes
ORDER BY date_commande DESC;

ORDER BY fonctionne sur tous les types : nombres (numérique), texte (alphabétique, sensible à la collation), dates (chronologique), et même les NULL (voir section 4). Le tri alphabétique dépend de la collation de la base — en UTF-8, les accents sont généralement triés correctement en français.

SECTION 02

Tri multi-colonnes

Quand tu tries par plusieurs colonnes, SQL utilise la première colonne comme tri principal. En cas d’égalité sur la première colonne, il utilise la deuxième pour départager, et ainsi de suite. Chaque colonne peut avoir son propre sens de tri (ASC ou DESC).

— Trier par ville (A→Z), puis par nom (A→Z) dans chaque ville
SELECT nom, ville, age FROM clients
ORDER BY ville ASC, nom ASC;

— ville | nom | age
— Lyon | Alice | 28
— Lyon | Bob | 34
— Paris | Charlie | 22
— Paris | Diana | 31

— Sens de tri mixte : ville croissante, âge décroissant
SELECT nom, ville, age FROM clients
ORDER BY ville ASC, age DESC;

— Trier par numéro de colonne (déconseillé mais possible)
SELECT nom, ville, age FROM clients
ORDER BY 2, 1; — 2 = ville, 1 = nom

Le tri par numéro de colonne (ORDER BY 2, 1) est fragile : si tu réorganises les colonnes du SELECT, les numéros changent silencieusement. Préfère toujours nommer les colonnes explicitement pour un code maintenable.

SECTION 03

Tri conditionnel (CASE)

Parfois tu as besoin d’un ordre de tri personnalisé qui ne correspond ni à l’ordre alphabétique ni à l’ordre numérique. Par exemple, trier des statuts dans un ordre métier précis (en_attente → payée → expédiée → livrée), ou afficher certaines lignes en priorité. Le CASE dans ORDER BY te permet de définir un tri sur mesure.

— Tri personnalisé : statuts dans un ordre métier
SELECT * FROM commandes
ORDER BY
CASE statut
WHEN ‘en_attente’ THEN 1
WHEN ‘payee’ THEN 2
WHEN ‘expediee’ THEN 3
WHEN ‘livree’ THEN 4
WHEN ‘annulee’ THEN 5
END;

— Afficher les VIP en premier, puis les autres par nom
SELECT * FROM clients
ORDER BY
CASE WHEN role = ‘vip’ THEN 0 ELSE 1 END,
nom ASC;

— MySQL : FIELD() — raccourci pour l’ordre personnalisé
SELECT * FROM commandes
ORDER BY FIELD(statut, ‘en_attente’, ‘payee’, ‘expediee’, ‘livree’, ‘annulee’);

SECTION 04

ORDER BY et les NULL

Le tri des NULL varie selon le SGBD — c’est un piège classique quand tu migres une requête d’un système à l’autre. En MySQL et SQL Server, les NULL sont considérés comme les plus petites valeurs (ils apparaissent en premier en ASC). En PostgreSQL et Oracle, les NULL sont considérés comme les plus grandes valeurs (ils apparaissent en dernier en ASC).

— Comportement par défaut (varie selon le SGBD)
SELECT nom, telephone FROM clients
ORDER BY telephone ASC;
— MySQL : NULL en premier (plus petit)
— PostgreSQL : NULL en dernier (plus grand)

— PostgreSQL : contrôler explicitement la position des NULL
SELECT * FROM clients
ORDER BY telephone ASC NULLS LAST; — NULL à la fin

SELECT * FROM clients
ORDER BY telephone DESC NULLS FIRST; — NULL au début

— MySQL : émuler NULLS LAST avec CASE ou IS NULL
SELECT * FROM clients
ORDER BY telephone IS NULL ASC, telephone ASC;
— IS NULL retourne 0 (non null) ou 1 (null)
— Les non-null (0) passent avant les null (1)

SGBD NULL en ASC NULL en DESC NULLS FIRST/LAST
MySQL En premier En dernier ❌ Non supporté (émuler avec IS NULL)
PostgreSQL En dernier En premier ✅ Supporté nativement
SQL Server En premier En dernier ❌ Non supporté
Oracle En dernier En premier ✅ Supporté nativement

SECTION 05

LIMIT — limiter le nombre de lignes

🔢 Récupérer les N premières lignes

LIMIT restreint le nombre de lignes retournées. C’est la dernière étape d’exécution d’une requête — elle s’applique après le tri (ORDER BY). Sans ORDER BY, LIMIT retourne N lignes dans un ordre non déterministe, ce qui est rarement ce que tu veux.

— Les 10 commandes les plus récentes
SELECT * FROM commandes
ORDER BY date_commande DESC
LIMIT 10;

— Le client avec le plus gros chiffre d’affaires
SELECT client, SUM(montant) AS ca
FROM commandes
GROUP BY client
ORDER BY ca DESC
LIMIT 1;

— Top 5 des produits les plus chers
SELECT nom, prix FROM produits
ORDER BY prix DESC
LIMIT 5;

🔀 Syntaxe selon le SGBD

La syntaxe pour limiter les résultats varie d’un SGBD à l’autre. C’est l’une des rares différences syntaxiques majeures entre les moteurs SQL :

— MySQL, PostgreSQL, SQLite
SELECT * FROM produits LIMIT 10;

— SQL Server
SELECT TOP 10 * FROM produits;

— Oracle (12c+)
SELECT * FROM produits
FETCH FIRST 10 ROWS ONLY;

— SQL standard (ANSI SQL:2008) — le plus portable
SELECT * FROM produits
ORDER BY prix DESC
FETCH FIRST 10 ROWS ONLY;

SECTION 06

Pagination avec OFFSET

📄 LIMIT + OFFSET = pagination

Pour afficher les résultats page par page (comme dans une liste de produits e-commerce), tu combines LIMIT (nombre de résultats par page) avec OFFSET (nombre de lignes à sauter). OFFSET 0 commence au début, OFFSET 10 saute les 10 premières lignes, etc.

— Page 1 : les 10 premiers résultats
SELECT * FROM produits
ORDER BY nom
LIMIT 10 OFFSET 0;

— Page 2 : résultats 11 à 20
SELECT * FROM produits
ORDER BY nom
LIMIT 10 OFFSET 10;

— Page 3 : résultats 21 à 30
SELECT * FROM produits
ORDER BY nom
LIMIT 10 OFFSET 20;

— Formule générale : OFFSET = (page – 1) * taille_page

— Syntaxe raccourcie MySQL (LIMIT offset, count)
SELECT * FROM produits
ORDER BY nom
LIMIT 10, 10; — LIMIT offset, count → page 2
— ⚠️ Confusant : le 1er nombre est l’offset, pas le count

⚠️ OFFSET a un problème de performance majeur : LIMIT 10 OFFSET 100000 force la base à lire 100 010 lignes et à en jeter 100 000. Plus l’OFFSET est grand, plus la requête est lente. C’est acceptable pour les premières pages, mais catastrophique pour les grandes tables avec une pagination profonde.

SECTION 07

Pagination performante (cursor-based)

🚀 L’alternative à OFFSET pour les grandes tables

Au lieu de sauter N lignes avec OFFSET, la pagination par curseur utilise la dernière valeur vue comme point de départ. Au lieu de dire « saute 1000 lignes », tu dis « donne-moi les lignes après l’ID 1000 ». La base utilise l’index et va directement au bon endroit — pas besoin de scanner les lignes précédentes.

— ❌ Pagination OFFSET (lente sur les pages profondes)
SELECT * FROM produits
ORDER BY id
LIMIT 10 OFFSET 100000; — lit 100 010 lignes

— ✅ Pagination par curseur (rapide, même sur les « pages » profondes)
— Page 1 : pas de condition
SELECT * FROM produits
ORDER BY id
LIMIT 10;
— Dernière ligne retournée : id = 10

— Page suivante : partir de la dernière valeur vue
SELECT * FROM produits
WHERE id > 10 — le curseur
ORDER BY id
LIMIT 10;

— Page encore après : id > dernier_id_de_la_page_précédente
SELECT * FROM produits
WHERE id > 20
ORDER BY id
LIMIT 10;

Critère OFFSET Cursor (WHERE id > last)
Performance ⚠️ Dégradée sur les pages profondes ✅ Constante quelle que soit la page
Accès direct à une page ✅ Oui (page 42 directement) ❌ Non (pages séquentielles uniquement)
Résultats stables si données changent ❌ Non (lignes décalées si INSERT/DELETE) ✅ Oui (basé sur la valeur, pas la position)
Complexité Simple Un peu plus complexe (stocker le curseur)

En pratique : OFFSET est parfait pour les petites tables et les premières pages. Pour les fils d’actualité infinis (Twitter, Reddit), les APIs, et les tables de millions de lignes, utilise la pagination par curseur. C’est le pattern utilisé par toutes les grandes apps.

SECTION 08

Erreurs fréquentes

Erreur Problème Solution
LIMIT sans ORDER BY Les N lignes retournées sont aléatoires Toujours combiner LIMIT avec ORDER BY
OFFSET sur des millions de lignes Performance catastrophique Pagination par curseur (WHERE id > last)
Tri sur une colonne sans index Tri en mémoire coûteux (filesort) Ajouter un index sur la colonne triée
ORDER BY dans une sous-requête Ignoré par certains SGBD (l’optimiseur le retire) ORDER BY uniquement dans la requête principale
Tri par numéro de colonne Fragile si les colonnes changent Nommer les colonnes explicitement
NULL mal placés dans le tri Comportement différent selon le SGBD NULLS FIRST/LAST ou IS NULL

SECTION 09

Questions fréquentes

ORDER BY est-il obligatoire pour avoir un résultat ordonné ?
Oui, absolument. Sans ORDER BY, SQL ne garantit aucun ordre. Même si les résultats semblent triés par ID en pratique (parce que c’est l’ordre du stockage), c’est un accident. Un UPDATE, un VACUUM, une migration, ou un changement de plan d’exécution peut changer l’ordre à tout moment.
Un index accélère-t-il ORDER BY ?
Oui, si l’index correspond à la colonne et au sens du tri. Un index B-Tree sur une colonne permet au SGBD de lire les données déjà triées, sans avoir à faire un tri en mémoire (filesort). C’est particulièrement important combiné avec LIMIT — la base peut s’arrêter après N lignes sans tout trier.
Peut-on ORDER BY une colonne absente du SELECT ?
Oui dans la plupart des cas. SELECT nom FROM clients ORDER BY age est valide — tu tries par age sans l’afficher. Exception : si tu utilises DISTINCT ou GROUP BY, tu ne peux ORDER BY que sur des colonnes qui apparaissent dans le SELECT ou dans un agrégat.
LIMIT et OFFSET sont-ils standard SQL ?
LIMIT et OFFSET sont supportés par MySQL, PostgreSQL et SQLite, mais ne font pas partie du standard SQL ANSI. Le standard utilise FETCH FIRST N ROWS ONLY et OFFSET N ROWS. SQL Server utilise TOP N. En pratique, LIMIT est le plus utilisé car MySQL et PostgreSQL dominent le marché.

ORDER BY et LIMIT en SQL — Trier et paginer

Référence : sql.sh ORDER BY