Quand une requête ne suffit pas
Certaines questions demandent un résultat intermédiaire. « Quels clients ont commandé plus que la moyenne » suppose de connaître la moyenne avant de comparer.
Deux outils répondent à ce besoin, et il vaut mieux savoir lequel choisir.
La sous-requête simple
SELECT nom, total
FROM client
WHERE total > (SELECT AVG(total) FROM client);
La sous-requête entre parenthèses renvoie une valeur unique, calculée une fois. C'est lisible et efficace.
Dans une clause IN, elle renvoie une liste :
SELECT nom FROM client
WHERE id IN (SELECT client_id FROM commande WHERE montant > 500);
Attention à un piège réel : si la sous-requête d'un NOT IN peut renvoyer une valeur nulle, le résultat global devient vide, sans erreur ni avertissement. Préférez NOT EXISTS dès qu'un nul est possible.
La sous-requête corrélée, à utiliser avec prudence
Elle référence la ligne de la requête externe, donc elle est réévaluée pour chaque ligne :
SELECT c.nom
FROM client c
WHERE EXISTS (SELECT 1 FROM commande o WHERE o.client_id = c.id);
Sur quelques milliers de lignes, aucun problème. Sur des millions, c'est le motif qui transforme une requête en attente interminable. Une jointure fait souvent le même travail bien plus vite.
L'expression de table commune
Pour enchaîner plusieurs étapes, elle est plus lisible qu'un empilement de parenthèses :
WITH gros_clients AS (
SELECT client_id, SUM(montant) AS total
FROM commande
GROUP BY client_id
HAVING SUM(montant) > 1000
)
SELECT c.nom, g.total
FROM gros_clients g
JOIN client c ON c.id = g.client_id
ORDER BY g.total DESC;
L'avantage n'est pas la performance mais la lecture : chaque étape porte un nom, et la requête se relit six mois plus tard.
La vue
Une vue est une requête enregistrée sous un nom, qu'on interroge comme une table.
CREATE VIEW commandes_du_mois AS
SELECT * FROM commande WHERE date_commande >= date_trunc('month', now());
Elle sert deux usages honnêtes : éviter de réécrire une requête compliquée, et exposer une partie seulement des données à certains utilisateurs, par exemple une table client sans les coordonnées personnelles.
Deux limites à connaître. Une vue ne stocke rien : interroger une vue coûte ce que coûte sa requête. Et empiler des vues sur des vues produit des requêtes dont personne ne prévoit plus le coût, situation classique des bases anciennes.
À retenir
Sous-requête simple pour une valeur intermédiaire, NOT EXISTS plutôt que NOT IN dès qu'un nul est possible, expression de table commune pour enchaîner lisiblement. Une vue nomme une requête, elle n'accélère rien, et les vues empilées coûtent cher.