Aller au contenu principal
bddSQL avancé

SQL avancé

Objectifs du Chapitre

Savoir filtrer les données avec des conditions simples ou multiples.

Utiliser efficacement les fonctions d'agrégation (COUNT, AVG, SUM, etc.).

Maîtriser les regroupements (GROUP BY) et les filtres d'agrégats (HAVING).

Formuler des requêtes imbriquées simples sans utiliser de jointures.

Où on va

Les requêtes simples suffisent tant qu'on interroge une table. Dès qu'il faut croiser, regrouper, comparer un résultat à un autre résultat, il faut savoir dans quel ordre le moteur travaille : WHERE avant le regroupement, HAVING après. Ce chapitre traite ce qui fait vraiment trébucher : les NULL, la différence WHERE / HAVING, et les sous-requêtes.

Introduction

Le chapitre précédent a posé les bases du SQL : créer des tables, insérer des données, faire des sélections simples et des jointures. Ce chapitre va plus loin : on apprend à interroger finement les données avec des conditions élaborées, des calculs statistiques, et des requêtes qui s'appellent entre elles.

Ces techniques sont celles que vous utiliserez le plus en pratique pour analyser et exploiter des bases de données réelles.

La grammaire d'une requête SQL

SELECT colonnes
FROM table
[WHERE condition]
[GROUP BY colonne]
[HAVING condition]
[ORDER BY colonne]
[LIMIT n];

Chaque clause joue un rôle précis. Ce qui rend SQL particulier : l'ordre d'écriture n'est pas l'ordre d'exécution.

OrdreClauseRôle
1FROMCharge la table source de données
2WHEREFiltre les lignes avant tout calcul
3GROUP BYRegroupe les lignes ayant des colonnes identiques
4HAVINGFiltre les groupes après GROUP BY
5SELECTCalcule et sélectionne les colonnes à afficher
6DISTINCTSupprime les doublons dans les résultats
7ORDER BYTrie les résultats affichés
8LIMITLimite le nombre de lignes retournées
Ordre d'exécution

La clause FROM est traitée en premier, même si elle est écrite après SELECT.
C'est pourquoi on ne peut pas utiliser un alias défini dans SELECT à l'intérieur d'un WHERE : quand WHERE s'exécute, SELECT n'a pas encore calculé l'alias.
En revanche, ORDER BY s'exécute après SELECT, donc les alias y sont accessibles.

Filtres avec WHERE

requete.sql
Résultat
>_ Prêt à exécuter…

Utilisation de plusieurs conditions avec AND, OR, BETWEEN, LIKE :

requete.sql
Résultat
>_ Prêt à exécuter…

Opérateurs disponibles dans WHERE

OpérateurDescriptionExemple
=Egal aPays = 'USA'
<> ou !=Different deAnneeSortie <> 2023
>, <, >=, <=Comparaison numerique ou alphabetiqueBudget >= 1000000
BETWEEN ... ANDValeur comprise dans un intervalle (bornes incluses)AnneeSortie BETWEEN 2000 AND 2010
IN (...)Appartenance a une listePays IN ('USA', 'France', 'UK')
NOT IN (...)Exclusion d'une listePays NOT IN ('Chine', 'Russie')
LIKECorrespondance partielle avec des jokersTitreFilm LIKE 'Star%'
IS NULLTeste si une valeur est nulleDateSortie IS NULL
IS NOT NULLTeste si une valeur est non nulleDateSortie IS NOT NULL

Utilisation de jokers avec LIKE

JokerSignificationExemple
%Remplace n'importe quelle suite de caracteres (y compris vide)'Mac%' trouve MacDonald, Macbeth, Mac
_Remplace exactement un seul caractere'M_c' trouve Mac, Mec, Mic

Combiner plusieurs conditions avec des parenthèses

-- Tous les films américains ou britanniques sortis après 2010
WHERE (Pays = 'USA' OR Pays = 'UK') AND AnneeSortie > 2010

Les parenthèses sont essentielles : sans elles, le AND étant prioritaire sur le OR, l'interprétation serait différente.

Le piège des valeurs NULL

Comparaison avec NULL

En SQL, NULL signifie "valeur inconnue". On ne peut pas comparer NULL avec = ou !=.

-- ❌ Ne retourne RIEN, même si des lignes ont DateSortie = NULL
WHERE DateSortie = NULL

-- ✅ Correct
WHERE DateSortie IS NULL

-- ❌ N'inclut pas les lignes avec NULL dans DateSortie
WHERE DateSortie != '2020-01-01'

-- ✅ Inclut les NULL
WHERE DateSortie != '2020-01-01' OR DateSortie IS NULL
A retenir

La clause WHERE filtre les lignes individuelles avant tout calcul.
Une bonne utilisation des operateurs permet d'extraire precisement les donnees voulues.

Fonctions d'agrégation

Les fonctions d'agrégation calculent une valeur à partir de plusieurs lignes. Elles sont incontournables pour produire des statistiques.

FonctionRoleRemarque
COUNT(*)Nombre total de lignesCompte toutes les lignes, y compris les NULL
COUNT(col)Nombre de valeurs non NULL dans colIgnore les NULL
SUM(col)Somme des valeursIgnore les NULL
AVG(col)Moyenne des valeursIgnore les NULL
MIN(col)Valeur minimaleFonctionne aussi sur des dates et textes
MAX(col)Valeur maximaleFonctionne aussi sur des dates et textes

Exemples sur l'ensemble de la table

requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…

Regrouper avec GROUP BY

GROUP BY divise les lignes en groupes selon une colonne, et les fonctions d'agrégation s'appliquent à chaque groupe séparément.

Visualiser l'effet de GROUP BY

Table FILM (extrait) :

TitreFilmPaysBudget
InceptionUSA160000000
InterstellarUSA165000000
ParasiteCoree11400000
TitanicUSA200000000
OkjaCoree50000000
requete.sql
Résultat
>_ Prêt à exécuter…

Résultat :

Paysnb_filmsbudget_moyen
USA3175000000
Coree230700000

Chaque groupe (USA, Coree) produit une seule ligne de résultat. Les 5 lignes d'origine sont résumées en 2.

requete.sql
Résultat
>_ Prêt à exécuter…

Règle fondamentale du GROUP BY

Dans un SELECT avec GROUP BY, chaque colonne affichée doit être soit :

  • dans la clause GROUP BY, soit une fonction d'agrégation (COUNT, AVG, etc.)

Sinon, la requête est invalide (ou retourne un résultat imprévisible selon le SGBD).

A retenir

Les fonctions d'agrétion s'utilisent dans la clause SELECT.
Elles calculent une valeur unique par groupe ou sur l'ensemble des lignes.
Pour les appliquer à plusieurs groupes, utilisez GROUP BY.
Pour filtrer les résultats agrégés, utilisez HAVING (pas WHERE).

Filtrer après regroupement : HAVING

HAVING s'applique après GROUP BY pour filtrer les groupes selon une condition sur les agrégats.

requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…

Combiner WHERE et HAVING

Les deux clauses peuvent coexister : elles n'agissent pas au même moment.

requete.sql
Résultat
>_ Prêt à exécuter…

Différence entre WHERE et HAVING

WHEREHAVING
QuandAvant le regroupementAprès le GROUP BY
Sur quoiLes lignes individuellesLes groupes
Agrégats autorisésNonOui (COUNT, AVG, etc.)

Exécute le bloc suivant : le moteur refuse la requête, et son message est exactement la leçon à retenir.

requete.sql
Résultat
>_ Prêt à exécuter…

La bonne écriture regroupe d'abord, puis filtre sur le résultat du regroupement :

requete.sql
Résultat
>_ Prêt à exécuter…
A retenir

Utilisez WHERE pour filtrer les lignes individuelles (avant regroupement).
Utilisez HAVING pour filtrer les groupes formés par GROUP BY.
Les deux peuvent être combinés dans une même requête - ils s'appliquent à des moments différents.

Requêtes imbriquées (sous-requêtes)

Une sous-requête est un SELECT placé à l'intérieur d'une autre requête. Elle est exécutée en premier, et son résultat est utilisé par la requête externe.

Les trois types de sous-requêtes

  • Scalaire : retourne une seule valeur - utilisée avec =, >, <...
  • Multi-valeur : retourne une colonne de valeurs - utilisée avec IN / NOT IN
  • Corrélée : fait référence à la requête externe, réévaluée ligne par ligne

Sous-requêtes scalaires

requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…
Sous-requête scalaire

Une sous-requête utilisée avec =, >, etc. doit retourner exactement une valeur.
Si elle retourne plusieurs lignes, le SGBD retourne une erreur.
Utilisez MAX(), MIN(), AVG() pour garantir un résultat unique.

Sous-requêtes avec IN et NOT IN

requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…
requete.sql
Résultat
>_ Prêt à exécuter…
Piège de NOT IN avec des NULL

Si la sous-requête retourne au moins un NULL, NOT IN ne retournera aucune ligne.
C'est un comportement logiquement cohérent mais contre-intuitif.
En cas de doute, préférez NOT EXISTS qui gère correctement les NULL.

Requêtes avancées sans jointure

Nombre de films par année (seulement si >= 3)

requete.sql
Résultat
>_ Prêt à exécuter…

Top 3 des années avec le plus gros budget cumulé

requete.sql
Résultat
>_ Prêt à exécuter…

Film le plus cher de chaque année (sous-requête corrélée)

requete.sql
Résultat
>_ Prêt à exécuter…

La sous-requête est corrélée : elle est réévaluée pour chaque ligne de f1, en cherchant le budget maximum de la même année.

Réalisateurs ayant sorti plus de films que la moyenne

requete.sql
Résultat
>_ Prêt à exécuter…
Méthode

Comment lire cette requête complexe

Elle se lit de l'intérieur vers l'extérieur :

  1. La sous-requête la plus interne calcule le nombre de films par réalisateur.
  2. La sous-requête intermédiaire calcule la moyenne de ces nombres.
  3. La requête externe ne garde que les réalisateurs dépassant cette moyenne.

Opérateurs ALL et ANY

-- Films strictement plus chers que TOUS les autres films de leur année
SELECT f1.TitreFilm, f1.AnneeSortie, f1.Budget
FROM FILM f1
WHERE f1.Budget > ALL (
  SELECT f2.Budget
  FROM FILM f2
  WHERE f2.AnneeSortie = f1.AnneeSortie AND f2.TitreFilm <> f1.TitreFilm
);
Non exécutable ici

ALL et ANY sont au standard SQL et fonctionnent sous MySQL et PostgreSQL, mais le moteur du navigateur (SQLite) ne les connaît pas. On les réécrit sans rien perdre : « supérieur à TOUS » revient à « supérieur au maximum », et « supérieur à AU MOINS UN » à « supérieur au minimum ».

requete.sql
Résultat
>_ Prêt à exécuter…
-- Films plus chers qu'AU MOINS UN film de 2010
SELECT TitreFilm, Budget
FROM FILM
WHERE Budget > ANY (
  SELECT Budget FROM FILM WHERE AnneeSortie = 2010
);
requete.sql
Résultat
>_ Prêt à exécuter…

ALL vs ANY

ALL : la condition doit être vraie pour tous les éléments de la sous-requête.
ANY (ou SOME) : la condition doit être vraie pour au moins un élément.
= ANY (...) est équivalent à IN (...).

A retenir

Les requêtes avancées peuvent combiner GROUP BY, HAVING, ORDER BY et sous-requêtes.
Les sous-requêtes corrélées permettent de croiser des données entre lignes sans jointure explicite.
Les opérateurs ALL et ANY permettent des comparaisons ensemblistes puissantes.

Tri des résultats : ORDER BY

requete.sql
Résultat
>_ Prêt à exécuter…

Trier par plusieurs colonnes

requete.sql
Résultat
>_ Prêt à exécuter…

L'ordre de tri est appliqué colonne par colonne : si deux films ont la même année, le budget départage.

Trier après agrégation

requete.sql
Résultat
>_ Prêt à exécuter…

On peut utiliser l'alias défini dans SELECT directement dans ORDER BY (car ORDER BY s'exécute après SELECT).

Trier les valeurs NULL

Le comportement par défaut des NULL varie selon le SGBD. Pour forcer leur position en MySQL :

-- Dates connues en premier, NULL en dernier
ORDER BY DateSortie IS NULL ASC, DateSortie ASC;

DateSortie IS NULL retourne 0 (faux) pour les valeurs renseignées et 1 (vrai) pour les NULL. En triant ASC, les 0 viennent avant les 1.

Bonnes pratiques ORDER BY
  • Utilisez ORDER BY pour faciliter la lecture et la hiérarchisation des résultats.
  • Combinez GROUP BY, HAVING, et ORDER BY pour construire des requêtes puissantes.
  • Utilisez toujours des aliases lisibles dans SELECT, surtout quand vous triez sur des agrégats.
  • ASC (croissant) est le comportement par défaut - ne l'écrivez que si vous voulez être explicite.