Les requêtes SQL
Ce que ce chapitre apporte4 points
- Comprendre le rôle du langage SQL dans la gestion des bases de données relationnelles.
- Créer une base de données et des tables avec des contraintes simples.
- Manipuler les données avec les instructions SELECT, INSERT, UPDATE et DELETE.
- Interroger les données avec des filtres, des tris, des jointures simples et des fonctions d'agrégation.
Jusqu'ici on a modélisé et raisonné sur les données. Ici on leur parle. SQL est un langage déclaratif : on décrit le résultat voulu, pas la marche à suivre, et c'est ce qui le rend à la fois court à écrire et déroutant au début. Les requêtes de ce chapitre s'exécutent directement dans la page, sur une base de démonstration : lire, modifier, relancer.
Chaque bloc SQL a un bouton Exécuter et un bouton Schéma. Modifier la requête, relancer, regarder ce qui change : c'est de loin la façon la plus rapide d'apprendre le langage. La base repart neuve à chaque exécution.
À quoi sert SQL ?
Les chapitres précédents ont montré comment modéliser une base de données (MCD, MLD) et raisonner sur les données avec l'algèbre relationnelle. SQL est la traduction concrète de tout cela : c'est le langage qu'on tape pour interroger une vraie base de données.
SQL (Structured Query Language) est un langage déclaratif utilisé pour interagir avec des bases de données relationnelles. Il permet de définir des structures, de manipuler des données, et d'interroger des informations rapidement et efficacement.
SQL est omniprésent dans les systèmes informatiques modernes. Il permet à la fois :
- De créer et structurer une base de données (
CREATE,ALTER,DROP) ; - D'ajouter, modifier ou supprimer des données (
INSERT,UPDATE,DELETE) ; - D'interroger et exploiter les données (
SELECT,JOIN,GROUP BY, etc.) ; - De contrôler les accès et la sécurité (
GRANT,REVOKE) ; - De générer des statistiques simples (moyennes, totaux, comptages).
Pourquoi le SQL est-il toujours utilisé aujourd'hui ?
- Standardisé : SQL est un langage reconnu par tous les grands systèmes de gestion de bases de données (MySQL, PostgreSQL, Oracle, SQL Server…).
- Lisible : sa syntaxe proche du langage naturel le rend facilement compréhensible.
- Puissant : il permet de traiter efficacement de très grandes quantités de données.
- Transverse : il est utilisé dans presque tous les métiers liés à la donnée.
- Informatique : authentification, gestion des comptes, suivi des activités utilisateur.
- BTP : planification des interventions, suivi des livraisons, gestion des équipements.
- Énergie : gestion des capteurs, alertes sur la consommation, historique des relevés.
- Transport : géolocalisation des véhicules, affectation des tournées, suivi des colis.
- Industrie : traçabilité des produits, contrôle qualité, gestion de la maintenance.
SQL est un outil essentiel pour manipuler et exploiter efficacement des données relationnelles. Il est utilisé dans tous les secteurs d'activité, quel que soit le volume ou le type d'information à gérer.
Types de données courants
| Type SQL | Description |
|---|---|
INT, BIGINT | Nombres entiers (positifs ou négatifs) |
SMALLINT, TINYINT | Entiers de plus petite taille (moins de mémoire utilisée) |
DECIMAL(p,s) | Nombres décimaux précis avec p chiffres au total et s après la virgule |
FLOAT, REAL | Nombres décimaux à virgule flottante (moins précis, plus légers) |
CHAR(n) | Texte fixe de longueur n (utile pour des codes, comme un code postal) |
VARCHAR(n) | Texte variable jusqu'à n caractères |
TEXT | Texte long (commentaires, descriptions...) |
DATE | Date au format AAAA-MM-JJ |
TIME, DATETIME | Heure seule ou date avec heure |
BOOLEAN | Valeur logique : TRUE ou FALSE |
ENUM(...) | Valeur choisie parmi une liste définie (ex : 'Homme', 'Femme') |
BLOB | Données binaires (images, fichiers, sons...) |
nom VARCHAR(50): chaîne de texte pouvant aller jusqu'à 50 caractères.montant DECIMAL(8,2): valeur numérique avec 6 chiffres avant la virgule et 2 après (ex : 123456.78).sexe ENUM('Homme', 'Femme', 'Autre'): contraint la valeur à une liste prédéfinie.codePostal CHAR(5): champ texte fixe pour un code à 5 chiffres.photo BLOB: stocke une image en base de données (rarement conseillé).
CHAR ou VARCHAR ?
Utiliser CHAR(n) quand la valeur a toujours exactement la même taille (code postal, numéro INSEE, identifiant fixe).
Utiliser VARCHAR(n) pour les textes de longueur variable (noms, adresses, titres).
CHAR est légèrement plus rapide ; VARCHAR économise de l'espace disque.
Familles de commandes SQL
Le langage SQL est divisé en plusieurs familles de commandes, selon leur rôle dans la gestion d'une base de données relationnelle.
| Catégorie | Signification | Exemples |
|---|---|---|
| DDL (Data Definition Language) | Définir ou modifier la structure des objets (tables, vues, clés). | CREATE, ALTER, DROP |
| DML (Data Manipulation Language) | Ajouter, modifier ou supprimer des données. | INSERT, UPDATE, DELETE |
| DQL (Data Query Language) | Interroger les données pour les lire ou les analyser. | SELECT |
| DCL (Data Control Language) | Gérer les permissions et la sécurité d'accès. | GRANT, REVOKE |
| TCL (Transaction Control Language) | Contrôler les transactions (groupes d'opérations atomiques). | COMMIT, ROLLBACK |
- DDL : Créer une nouvelle table dans la base.
- DML : Ajouter un étudiant ou modifier son prénom.
- DQL : Rechercher tous les étudiants nés après 2001.
- DCL : Donner à un utilisateur le droit de modifier la table
Etudiant. - TCL : Annuler une série d'insertions en cas d'erreur (
ROLLBACK). Le chapitre Les transactions y revient en détail, et montre ce qui se passe quand deux clients écrivent en même temps.
Les commandes DML et DQL sont les plus courantes pour interagir avec les données au quotidien. Les commandes DDL et DCL sont essentielles pour la structure et la sécurité de la base.
Structure d'une base de données
Création d'une base de données
La commande CREATE DATABASE crée une base nommée Ecole. La commande USE permet de travailler à l'intérieur de cette base. Toutes les commandes suivantes s'appliqueront à cette base tant qu'on ne change pas de contexte.
Création d'une table
PRIMARY KEY: identifie chaque ligne de façon unique.AUTO_INCREMENT: la valeur deidEtudiantest générée automatiquement (1, 2, 3…). Plus besoin de la saisir manuellement.NOT NULL: le champ est obligatoire, il ne peut pas rester vide.UNIQUE: deux lignes ne peuvent pas avoir la même valeur (ici, deux étudiants ne peuvent pas avoir le même email).DEFAULT TRUE: si on n'indique pas de valeur pouractif, il vautTRUEpar défaut.
Ce que la table contient une fois remplie. Les contraintes ne se voient pas dans les lignes, elles se voient dans ce qui a été refusé à l'insertion. Voici deux lignes acceptées :
| idEtudiant | nom | prenom | dateNaissance | actif | |
|---|---|---|---|---|---|
| 1 | Martin | Alice | 2001-05-12 | alice@ecole.fr | 1 |
| 2 | Durand | Lucas | 2000-11-03 | lucas@ecole.fr | 1 |
idEtudiant a été généré automatiquement grâce à AUTO_INCREMENT.
Autres contraintes utiles
CHECK: impose une condition logique sur la valeur (ici, le volume horaire doit être positif).DEFAULT 1.0: si aucun coefficient n'est précisé à l'insertion, la valeur 1.0 est utilisée automatiquement.
Bien définir ses contraintes dès la création des tables permet de garantir la cohérence et la qualité des données dans la base. Une contrainte rejetée au niveau de la base vaut mieux qu'une erreur découverte dans l'application.
Modifier la structure d'une table : ALTER TABLE
Après la création, il est possible de modifier la structure d'une table sans la supprimer.
ALTER TABLE fait partie du DDL. L'utiliser quand le schéma doit évoluer sans perdre les données existantes. En production, ces modifications doivent être planifiées avec soin (risque de perte de données si on supprime une colonne).
Manipuler les données avec SQL
Insertion de données : INSERT
Une ligne est ajoutée à Etudiant. idEtudiant n'est pas précisé, il est généré automatiquement ; les colonnes non mentionnées, ici actif, prennent leur valeur par défaut.
Le bloc n'affiche aucun tableau, et c'est normal : un INSERT modifie la table et ne rend aucune ligne. Pour voir le résultat, ajouter SELECT * FROM Etudiant WHERE nom = 'Durand'; sous l'insertion et relancer : Claire y apparaît avec l'identifiant 11 et actif à 1.
Insérer plusieurs lignes à la fois :
Sans nommer les colonnes (déconseillé) :
Il est toujours préférable de préciser les colonnes dans l'ordre d'insertion. Cela rend le code plus lisible, robuste et évite des erreurs en cas de changement dans la structure de la table.
Lecture des données : SELECT
Une requête s'écrit de haut en bas, SELECT sur la première ligne. Elle ne se construit pas dans cet ordre, et confondre les deux est la source d'erreur la plus fréquente au début.
FROMd'abord : dire d'où viennent les lignes. Sans table, rien d'autre n'a de sens.WHEREensuite : dire lesquelles garder. À ce stade, toutes les colonnes de la table sont disponibles pour le filtre, y compris celles qui ne seront jamais affichées.SELECTen dernier : dire lesquelles afficher. C'est un choix d'affichage, pas un choix de données.
Commencer par SELECT mène à deux erreurs jumelles : filtrer sur une colonne qu'on a oublié de garder, ou croire qu'une colonne absente du SELECT est aussi absente du WHERE. Le moteur, lui, exécute dans ce même ordre de construction, ce qui a des conséquences visibles plus loin dans le chapitre.
Construite dans l'ordre : partir de Etudiant, ne garder que les étudiants nés après le 1er janvier 2001, n'afficher que le nom et le prénom, trier par nom croissant.
Le résultat contient dix lignes et deux colonnes, c'est-à-dire toute la table : sur cette base de démonstration, aucun étudiant n'est né avant 2001, et le filtre n'écarte donc personne. Remplacer 2001 par 2004 dans le bloc et relancer : le résultat tombe à cinq lignes. Un filtre qui ne retire rien n'est pas un filtre qui ne marche pas.
Opérateurs de filtre courants
| Opérateur | Description | Exemple |
|---|---|---|
=, !=, <, > | Comparaison classique | WHERE note > 10 |
BETWEEN ... AND ... | Dans un intervalle (bornes incluses) | WHERE note BETWEEN 10 AND 15 |
IN (...) | Parmi une liste de valeurs | WHERE ville IN ('Lyon', 'Paris') |
LIKE | Correspondance avec un motif | WHERE nom LIKE 'Du%' |
IS NULL / IS NOT NULL | Valeur absente ou présente | WHERE email IS NOT NULL |
Le bloc contient trois requêtes et affiche donc trois tableaux. La première rend deux lignes, Dupond et Durand. La deuxième en rend deux également, Bernard et Marchand, les deux étudiants dont l'email est absent. La troisième rend onze lignes sur les vingt-cinq inscriptions : les bornes sont incluses, une note de 10 ou de 15 est retenue.
Le caractère joker % dans LIKE.
% remplace n'importe quelle suite de caractères (y compris vide).
_ remplace exactement un caractère.
Exemples : 'Du%' → commence par "Du" | '%on' → finit par "on" | 'D_pont' → "Dupont", "Damont", etc.
Afficher des valeurs distinctes : DISTINCT
DISTINCT supprime les doublons du résultat. Client compte quatre lignes et deux clients habitent Paris ; le résultat contient trois lignes et une colonne, Paris, Lyon et Bordeaux. Le dédoublonnage coûte cher au moteur, qui ne le fait que si on le demande.
Limiter le nombre de résultats : LIMIT
LIMIT coupe le résultat après le nombre de lignes indiqué, une fois le tri appliqué. Ici la table compte exactement dix étudiants : le résultat en contient dix et la coupe ne retire rien. Passer à LIMIT 3 pour la voir agir, ce qui laisse Bernard, Diallo et Dupond, les trois premiers dans l'ordre alphabétique.
- Éviter
SELECT *dans les applications : préférer lister les colonnes utiles. - Utiliser
WHEREpour limiter le volume de données retourné. - Trier avec
ORDER BYpour faciliter la lecture des résultats. - Utiliser
LIMITpour ne pas surcharger l'application lors des tests.
Mise à jour : UPDATE
Le nom et l'email de l'étudiant 2 sont modifiés. Plusieurs colonnes se modifient d'un coup, séparées par des virgules dans SET.
Comme pour l'insertion, aucun tableau ne s'affiche : le bloc annonce une ligne modifiée, rien de plus. Le seul moyen de vérifier une modification est de relire la table ensuite.
Toujours utiliser WHERE pour cibler les lignes à modifier. Un UPDATE Etudiant SET nom = 'Dupond' sans WHERE modifie tous les étudiants de la table, et cette erreur est irrécupérable sans sauvegarde.
Suppression : DELETE
Seul l'étudiant 4 est supprimé. Filtrer sur la clé primaire est la façon la plus sûre de ne viser qu'une ligne, puisqu'aucune autre ne peut porter la même valeur.
Là encore, le bloc ne rend aucun tableau : il annonce une ligne supprimée.
Supprimer toutes les données de la table (dangereux) :
DELETE vs TRUNCATE
DELETE sans WHERE supprime toutes les lignes mais conserve la structure et peut être annulé avec ROLLBACK.
TRUNCATE vide la table et remet l'AUTO_INCREMENT à 1. L'opération n'est pas annulable, et se réserve aux environnements de développement.
Requêtes avec jointures
Une jointure permet de combiner des données provenant de plusieurs tables. C'est l'opération clé dans une base relationnelle, où les informations sont réparties sur plusieurs tables reliées par des clés.
Données d'exemple
Pour illustrer les jointures, imaginons ces deux tables :
Table Etudiant :
| idEtudiant | nom | prenom |
|---|---|---|
| 1 | Martin | Alice |
| 2 | Durand | Lucas |
| 3 | Leroy | Emma |
Inscription (idCours = 10 = "Maths", 20 = "SQL")
| idEtudiant | idCours |
|---|---|
| 1 | 10 |
| 1 | 20 |
| 2 | 10 |
Emma (id=3) n'a aucune inscription. Alice (id=1) en a deux. Ces deux particularités ne sont pas décoratives : ce sont elles qui font toute la différence entre les types de jointure.
La figure suivante prend ces deux tables et les joint des cinq façons possibles. L'appariement, lui, ne change jamais : ce qui change est le sort réservé aux lignes qui n'ont trouvé personne. Basculer d'un onglet à l'autre pour voir les orphelines apparaître et disparaître.
INNER JOIN … ON ne garde que les lignes appariées des deux côtés.
Etudiant(gauche, 3 lignes)
| idEtudiant | nom | prenom |
|---|---|---|
| 1 | Martin | Alice |
| 2 | Durand | Lucas |
| 3 | Leroy | Emma |
Inscription(droite, 3 lignes)
| idEtudiant | idCours |
|---|---|
| 1 | 10 |
| 1 | 20 |
| 2 | 10 |
ON Etudiant.idEtudiant = Inscription.idEtudiant
| Etudiant.idEtudiant | nom | prenom | Inscription.idEtudiant | idCours |
|---|---|---|---|---|
| 1 | Martin | Alice | 1 | 10 |
| 1 | Martin | Alice | 1 | 20 |
| 2 | Durand | Lucas | 2 | 10 |
- Une ligne de gauche appariée à plusieurs lignes de droite se répète autant de fois : une jointure peut rendre plus de lignes qu'aucune des deux tables n'en contient.
SELECT *
FROM Etudiant
INNER JOIN Inscription
ON Etudiant.idEtudiant = Inscription.idEtudiantTrois faits se lisent directement sur cette figure.
- Alice se dédouble. Elle a deux inscriptions, elle apparaît donc deux fois. Une jointure peut rendre plus de lignes que la table de départ n'en contient, ce qui surprend systématiquement la première fois.
- Emma disparaît en
INNER, revient enLEFT. Elle n'a aucune inscription : la jointure interne l'écarte, la jointure externe gauche la garde en complétant sa droite par desNULL. - Ces
NULLsont fabriqués. Aucune ligne de la base ne contient cette valeur. C'est la jointure qui l'invente pour combler la place d'une ligne qui n'existe pas, et c'est une source d'erreurs classique dans les calculs qui suivent.
Jointure interne (INNER JOIN)
Construite dans l'ordre : partir de Etudiant, l'apparier à Inscription sur l'identifiant d'étudiant, puis n'afficher que le nom, le prénom et le cours.
Sur les trois étudiants de la figure ci-dessus, le résultat compterait trois lignes :
| nom | prenom | idCours |
|---|---|---|
| Martin | Alice | 10 |
| Martin | Alice | 20 |
| Durand | Lucas | 10 |
Le bloc, lui, ne travaille pas sur ces trois lignes : il s'exécute sur la base de démonstration, qui compte dix étudiants et vingt-cinq inscriptions. Il rend donc vingt-cinq lignes et trois colonnes, une ligne par inscription appariée. Le principe est le même dans les deux cas.
Une JOIN (ou INNER JOIN) ne conserve que les lignes ayant une correspondance dans les deux tables. Emma n'apparaît pas car elle n'a aucune inscription.
Jointure externe gauche (LEFT JOIN)
Sur les données de la figure, le résultat compterait quatre lignes, une de plus que la jointure interne :
| nom | prenom | idCours |
|---|---|---|
| Martin | Alice | 10 |
| Martin | Alice | 20 |
| Durand | Lucas | 10 |
| Leroy | Emma | NULL |
Une LEFT JOIN conserve toutes les lignes de la table de gauche (Etudiant), même sans correspondance à droite. Emma apparaît avec NULL dans idCours.
Exécuté sur la base de démonstration, ce bloc rend exactement les mêmes vingt-cinq lignes que le précédent. La raison compte : aucun étudiant de cette base n'est sans inscription, il n'y a donc aucune orpheline à rattraper. La différence entre les deux jointures ne se voit que lorsqu'une ligne reste seule, et c'est pour cela que la figure invente Emma.
Jointure sur 3 tables
Les jointures se chaînent pour remonter les données à travers la table de liaison Inscription : d'abord l'étudiant vers ses inscriptions, puis chaque inscription vers son cours. Le résultat contient vingt-cinq lignes et deux colonnes, le nom de l'étudiant et l'intitulé du cours. Un étudiant y figure autant de fois qu'il suit de cours, ce qu'il faut garder en tête avant de compter quoi que ce soit sur ce résultat.
Résumé des types de jointures
| Type de jointure | Résultat attendu | Sur la figure ci-dessus |
|---|---|---|
INNER JOIN | seulement les lignes appariées des deux côtés | 3 lignes |
LEFT JOIN | toutes les lignes de gauche, droite complétée par des NULL | 4 lignes, Emma revient |
RIGHT JOIN | toutes les lignes de droite, gauche complétée par des NULL | 3 lignes, aucune inscription n'est orpheline |
FULL JOIN | toutes les lignes des deux tables | 4 lignes, non supporté nativement par MySQL |
CROSS JOIN | chaque ligne de gauche avec chaque ligne de droite, sans condition | 9 lignes, 3 × 3 |
- Utiliser
INNER JOINpour ne garder que les données qui ont une correspondance dans toutes les tables. - Utiliser
LEFT JOINpour garder tous les enregistrements de la table principale, même sans correspondance. RIGHT JOINest rarement utilisé : on préfère inverser l'ordre des tables et utiliserLEFT JOIN.
Requêtes avec agrégats
Une jointure rend une ligne par appariement, et ce compte devient vite trop gros pour être lu. La suite consiste à le réduire à un résumé.
Les fonctions d'agrégation font un calcul sur plusieurs lignes et rendent une seule valeur. Elles transforment une liste en statistique.
Fonctions d'agrégation disponibles
| Fonction | Rôle | Exemple |
|---|---|---|
COUNT() | Compte le nombre de lignes | COUNT(*), COUNT(idEtudiant) |
SUM() | Somme les valeurs | SUM(montant) |
AVG() | Calcule la moyenne | AVG(note) |
MAX() | Renvoie la valeur maximale | MAX(note) |
MIN() | Renvoie la valeur minimale | MIN(note) |
COUNT(*) ou COUNT(colonne) ?
COUNT(*) compte toutes les lignes, y compris celles dont les colonnes sont à NULL.
COUNT(colonne) ne compte que les lignes où cette colonne n'est pas à NULL.
L'écart entre les deux comptes est donc le nombre de valeurs manquantes. C'est le moyen le plus rapide de savoir si une colonne est bien remplie.
Renommer une colonne avec AS
AS donne un alias lisible à la colonne résultante. C'est particulièrement utile avec les agrégats, qui n'ont pas de nom par défaut.
Le résultat contient une seule ligne et deux colonnes : 25 inscriptions, et une moyenne de 12,91. Deux pièges tiennent dans ces deux nombres. Le compte porte sur les lignes de Inscription, donc sur les inscriptions et non sur les étudiants, qui sont dix ; nommer cette colonne total_etudiants aurait fait mentir le résultat. Et la moyenne ne porte que sur 23 notes, parce que deux inscriptions n'en ont pas et que AVG ignore les NULL au lieu de les compter pour zéro.
Regrouper des résultats : GROUP BY
Le nombre d'inscrits et la moyenne sont maintenant calculés pour chaque cours. Le résultat contient sept lignes, une par cours de la base, et trois colonnes. GROUP BY ne filtre rien : il découpe les lignes en paquets, et chaque fonction d'agrégation travaille ensuite à l'intérieur d'un paquet.
Filtrer les groupes : HAVING
Le résultat contient cinq lignes au lieu de sept : deux cours sortent, leur moyenne ne dépassant pas 12. Ce sont bien des groupes qui ont été écartés, pas des lignes.
WHERE ou HAVING ?
WHERE filtre les lignes avant le regroupement : il s'applique aux données brutes.
HAVING filtre les groupes après le GROUP BY : il s'applique aux résultats agrégés.
WHERE AVG(note) > 12 n'existe pas, et la raison est la même que pour l'ordre de construction vu plus haut : au moment où WHERE s'applique, les groupes ne sont pas formés et la moyenne n'existe pas encore.
Exemple complet avec jointure + agrégat
Cette requête enchaîne les quatre gestes du chapitre :
- Assembler
CoursetInscriptionsur l'identifiant de cours ; - Regrouper par intitulé, pour obtenir un paquet de lignes par cours ;
- Écarter les groupes dont la moyenne est inférieure à 10 ;
- Trier ce qui reste par moyenne décroissante.
Le résultat contient sept lignes et trois colonnes : sur cette base, les sept cours atteignent tous la moyenne de 10, et le HAVING n'écarte donc personne. Remplacer 10 par 13 et relancer : il reste quatre cours, Mathematiques en tête avec 14,4.
Ordre d'exécution d'une requête SQL
L'ordre de construction annoncé au début du chapitre, FROM puis WHERE puis SELECT, n'était pas un conseil de rédaction : c'est l'ordre dans lequel le moteur travaille. Le voici en entier, les clauses vues depuis intercalées à leur place.
| Ordre d'exécution | Clause | Rôle |
|---|---|---|
| 1 | FROM + JOIN | Charge les tables et les assemble |
| 2 | WHERE | Filtre les lignes |
| 3 | GROUP BY | Regroupe les lignes restantes |
| 4 | HAVING | Filtre les groupes |
| 5 | SELECT | Sélectionne les colonnes (et applique les agrégats) |
| 6 | DISTINCT | Supprime les doublons |
| 7 | ORDER BY | Trie les résultats |
| 8 | LIMIT | Limite le nombre de lignes retournées |
Ce que ce tableau explique. Un alias défini dans SELECT ne peut pas servir dans WHERE : à l'étape 2, l'étape 5 n'a pas eu lieu et l'alias n'existe pas.
Le même alias fonctionne en revanche dans ORDER BY, qui vient après. Les deux règles ne s'apprennent pas séparément : elles se déduisent de la colonne de gauche.
La figure suivante fait tourner une requête à jointure et agrégat, étape par étape. Cliquer sur chaque clause montre la relation à ce moment précis.
SELECT nomClasse, COUNT(*) AS effectifFROM EtudiantJOIN Classe ON Etudiant.idClasse = Classe.idClasseWHERE actif = 1GROUP BY nomClasseHAVING COUNT(*) >= 3ORDER BY effectif DESCOrdre d'exécution. Cliquer sur une étape pour voir ce qu'elle produit.
| nomClasse | effectif |
|---|---|
| 1A | 3 |
| 2A | 3 |
Suivre la clause HAVING est instructif : elle porte sur COUNT(*), une valeur qui n'existe qu'à partir de l'étape 3. L'écrire dans WHERE reviendrait à la demander à l'étape 2, avant qu'elle soit calculable, et le moteur refuse.
Créer une table avec une clé étrangère
- Clé primaire composite : le couple
(idEtudiant, idCours)est unique - un étudiant ne peut s'inscrire qu'une seule fois à un même cours. - FOREIGN KEY : crée un lien vers une autre table. Le SGBD vérifie que la valeur existe bien dans la table référencée.
- ON DELETE CASCADE : si un étudiant est supprimé, ses inscriptions sont automatiquement supprimées aussi.
- ON DELETE RESTRICT : si un cours est supprimé mais a des inscrits, la suppression est bloquée. Protection contre les suppressions accidentelles.
Ce que garantit l'intégrité référentielle. Sous MySQL, la seconde insertion ci-dessous est refusée avant même d'être écrite :
-- Accepté : l'étudiant 1 et le cours 10 existent tous les deux
INSERT INTO Inscription VALUES (1, 10, 14.5);
-- Refusé : l'étudiant 99 n'existe pas dans la table Etudiant
INSERT INTO Inscription VALUES (99, 10, 12.0);
-- Error: Cannot add or update a child row: a foreign key constraint fails
Ce bloc est en lecture seule, contrairement aux autres : le moteur embarqué dans cette page ne vérifie pas les clés étrangères, et laisserait passer les deux insertions sans rien dire. C'est justement ce que garantit un vrai SGBD et que la page ne peut pas montrer. Une contrainte non vérifiée par le moteur devient une contrainte que personne ne respecte.
Les clés étrangères garantissent l'intégrité référentielle : on ne peut pas créer de liens vers des données inexistantes.
Choisir entre ON DELETE CASCADE (suppression en cascade) et ON DELETE RESTRICT (blocage) selon le comportement souhaité.
La méthode
- Écrire
FROMen premier, avec les tables nécessaires et leurs alias. Une table qui n'apporte ni colonne affichée ni colonne filtrée est de trop. - Poser chaque jointure avec son
ONdès queFROMcite plus d'une table. Une jointure oubliée ne provoque aucune erreur : elle produit un produit cartésien, et le nombre de lignes explose sans prévenir. - Écrire
WHEREensuite, en filtrant sur les colonnes brutes. Toutes les colonnes des tables sont disponibles ici, y compris celles qui ne seront pas affichées. - Regrouper puis filtrer les groupes quand la question porte sur un résumé :
GROUP BYsur les colonnes qui identifient un paquet,HAVINGsur les fonctions d'agrégation, jamais l'inverse. - Écrire
SELECTen dernier, en nommant les colonnes une à une et en donnant un alias à chaque agrégat. Un alias qui décrit mal ce qui est compté fait mentir le résultat. - Ajouter
ORDER BYpuisLIMITen toute fin, pour la lecture seulement : ils ne changent jamais quelles lignes sont retenues, seulement lesquelles sont montrées. - Compter le résultat attendu avant de lancer. Un résultat plus long que prévu signale une jointure qui dédouble ; un résultat vide signale un filtre trop strict ou un
NULLoublié. - Relire tout
UPDATEet toutDELETEen cherchant sonWHEREavant de l'exécuter. C'est le seul geste de cette liste qui protège des données.
Synthèse
- SQL est un langage déclaratif : une requête décrit le résultat voulu, jamais la marche à suivre. C'est le SGBD qui choisit comment l'obtenir.
- Les commandes se rangent en familles : le DDL définit les structures, le DML modifie les données, le DQL les interroge, le DCL gère les droits, le TCL les transactions. Au quotidien, le DML et le DQL font l'essentiel.
- Créer une table consiste à nommer ses colonnes, leur donner un type et leur attacher des contraintes.
PRIMARY KEY,NOT NULL,UNIQUE,CHECKetDEFAULTfont refuser par la base ce qui serait découvert trop tard dans l'application. - Une clé étrangère relie une table à une autre et garantit l'intégrité référentielle : aucune ligne ne peut désigner une ligne inexistante.
ON DELETE CASCADEpropage la suppression,ON DELETE RESTRICTla bloque. INSERT,UPDATEetDELETEne rendent aucun tableau : ils modifient la table et annoncent un nombre de lignes touchées. UnUPDATEou unDELETEsansWHEREs'applique à toute la table.- Une requête se construit dans l'ordre
FROM,WHERE,GROUP BY,HAVING,SELECT,ORDER BY,LIMIT, qui est aussi l'ordre dans lequel le moteur l'exécute. L'ordre d'écriture, lui, commence parSELECT: les confondre explique la plupart des erreurs de débutant. - Une jointure interne ne garde que les lignes appariées des deux côtés ; une jointure externe garde les orphelines d'un côté et fabrique des
NULLpour l'autre. CesNULLn'existent nulle part dans la base. - Une jointure peut rendre plus de lignes que la table de départ n'en contient, puisqu'une ligne de gauche appariée trois fois produit trois lignes. Tout comptage fait ensuite sur ce résultat porte sur les appariements, pas sur les lignes d'origine.
- Les fonctions d'agrégation résument un paquet de lignes en une valeur.
COUNT(*)compte les lignes,COUNT(colonne)seulement celles qui portent une valeur, etAVG,SUM,MIN,MAXignorent lesNULLau lieu de les compter pour zéro. WHEREfiltre des lignes avant le regroupement,HAVINGfiltre des groupes après. Écrire une fonction d'agrégation dansWHEREest refusé, parce qu'à cette étape les groupes n'existent pas encore.
Quiz
1.Que fait une jointure interne aux lignes qui n'ont pas de correspondance ?
2.Une ligne de gauche s'apparie à trois lignes de droite. Combien de lignes produit-elle ?
3.D'où vient un NULL apparu dans le résultat d'un LEFT JOIN, à droite ?
4.Pourquoi ne peut-on pas employer dans WHERE un alias défini dans SELECT ?
5.DELETE FROM Etudiant; sans clause WHERE fait quoi ?
6.Quelle contrainte garantit qu'une colonne ne contient jamais deux fois la même valeur, tout en acceptant qu'elle soit absente ?
7.Une jointure sur trois tables passe par une table de liaison. Pourquoi ?
Ressources complémentaires
Mettre en pratique
Jointures, ordre d'exécution d'une requête, et cardinalités.
- Combien de lignes une jointure renvoieNiveau 2
- L'ordre dans lequel une requête s'exécuteNiveau 2
- Traduire une règle de gestion en cardinalitésNiveau 2
Filtrer, trier, tester l'absence de valeur, et choisir la jointure qui garde les bonnes lignes.
- Filtrer puis trierNiveau 1
- Ceux qui n'ont pas d'adresseNiveau 1
- Des bornes inclusesNiveau 2
- Garder les cours sans enseignantNiveau 2
- Traverser la table de liaisonNiveau 3
- Ceux qui ne sont inscrits nulle partNiveau 3