SELECT
Progression
#SELECT
SELECT sert à « poser une question » au moteur: quelles colonnes (projection), quelles lignes (filtre), dans quel ordre (tri) et combien (limite). Au‑delà de la syntaxe, comprendre l’ordre logique d’évaluation et le traitement des NULL permet d’écrire des requêtes correctes et performantes.
#Pourquoi et comment
- Ordre logique d’exécution:
FROM
→WHERE
→GROUP BY
→HAVING
→SELECT
→ORDER BY
→LIMIT
. Retenez queWHERE
ne voit pas les alias duSELECT
(qui arrivent plus tard), maisORDER BY
oui.
- NULL et comparaisons:
NULL
n’est pas égal àNULL
et=
avecNULL
retourne inconnu; utilisezIS NULL
/IS NOT NULL
.COUNT(col)
ignoreNULL
, alors queCOUNT(*)
compte toutes les lignes. - Tri et collation:
ORDER BY
dépend de la collation (sensible/insensible à la casse, accents). Soyez explicite si nécessaire. - Pagination:
LIMIT/OFFSET
est simple mais coûteux pour de grands offsets (scan et saut). Préférez la pagination par curseur/clé (keyset pagination) avec un prédicatWHERE col > dernière_valeur
. - Sous‑requêtes vs JOIN: une sous‑requête corrélée est lue conceptuellement pour chaque ligne; un
JOIN
exprime la relation directement. Préférez l’écriture la plus claire, puis mesurez.
1select id, name from users where id >= 1 order by id desc limit 5;
Colonnes calculées et alias:
1select id, upper(name) as NAME_UPPER from users;
Donnez des alias explicites aux expressions et utilisez un style cohérent pour les noms. order by NAME_UPPER
est permis car ORDER BY
voit les alias du SELECT
.
SARGability: écrivez vos prédicats pour qu’ils exploitent les index. Évitez d’envelopper la colonne indexée dans une fonction côté gauche du comparateur (where date(ts)=...
bloque l’index). Préférez des bornes (ts >= ... and ts < ...
). Harmonisez les types pour éviter des casts implicites qui empêchent le moteur d’utiliser l’index.
La pagination par curseur évite les grands sauts coûteux d’OFFSET
. Gardez un ordre strict et pagez par comparaison sur la dernière valeur vue.
1-- Keyset pagination: page suivante après (last_id, last_created_at)2select id, created_at, name3from users4where (created_at, id) > (?, ?)5order by created_at, id6limit 20;
#Playground
#Exercice : Sous-requêtes dans SELECT
Créez une requête qui affiche pour chaque utilisateur son nom et le nombre total de commandes qu'il a passées, en utilisant une sous-requête dans la clause SELECT.
#Instructions
- Supposez que vous avez deux tables :
users(id, name)
orders(id, user_id, amount)
- Utilisez une sous-requête dans la clause SELECT pour compter le nombre de commandes pour chaque utilisateur.
- La sous-requête doit compter les commandes de l'utilisateur courant dans la boucle principale.
#Exemple de requête
1-- Création des tables de démonstration2create table users(id int, name text);3create table orders(id int, user_id int, amount real);4 5-- Insertion de données6insert into users values (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');7insert into orders values (1, 1, 100.0), (2, 1, 50.0), (3, 2, 200.0);8 9-- Requête avec sous-requête dans SELECT10select 11 name,12 (select count(*) from orders where user_id = users.id) as order_count13from users;
#À retenir
- Ordre logique: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
- Utiliser des alias explicites pour la lisibilité.
- Utiliser
= NULL
au lieu deIS NULL
. - Référencer un alias du SELECT dans WHERE (non visible à ce stade).
- Paginer avec un grand OFFSET sans index de tri adéquat.