Objectifs d’apprentissage — à l’issue de la séance, vous serez capable de :
NULL) ;PRIMARY KEY, FOREIGN KEY,
UNIQUE, NOT NULL, CHECK,
DEFAULT) ;NULL et les pièges qu’il introduit
dans les comparaisons.Prérequis mobilisés : séance 1 (vocabulaire, lecture du schéma bibliothèque), programmation objet (notion d’attribut typé, d’identité d’objet).
Matériel : la base bibliotheque
importée en séance 1, un client mysql, et un
second script à fournir : schema.sql pour
recréer la structure sans données.
En TP lors de la séance 1 trois requêtes ont été rejetées : insertion d’un livre avec auteur inexistant, suppression d’un auteur référencé, insertion d’un auteur sans nom. Ces refus étaient particulièrement pertinents car ils ont assuré l’intégrité des données de la base. « Mais où sont donc écrites les règles à l’origine de ces refus ? » : dans le Data Definition Language (DDL)), c’est-à-dire dans le schéma de la base de données. C’est le fil de cette séance : le schéma est du code déclaratif qui protège les données mieux que n’importe quel code applicatif, parce qu’il s’applique à tous les programmes qui utilisent la base.
Plan de la séance: (1) les types, (2) NULL, (3) les
contraintes, (4) modifier un schéma.
On ne cherche pas l’exhaustivité : on couvre les types réellement utiles et on renvoie à la documentation pour le reste. On reste sur MariaDB/MySQL puisque c’est le SGBD du cours, en signalant les équivalents PostgreSQL entre parenthèses.
Types numériques.
| Type | Usage | Remarque |
|---|---|---|
TINYINT(1) |
booléen (0/1) | convention MySQL/MariaDB ; en PostgreSQL : BOOLEAN |
INT |
identifiants, compteurs | 4 octets, ±2 milliards |
BIGINT |
identifiants à forte volumétrie | 8 octets |
DECIMAL(p,s) |
montants monétaires | exact ; jamais FLOAT pour de
l’argent |
FLOAT/DOUBLE |
mesures scientifiques | approximation binaire |
Point à marteler : DECIMAL pour l’argent, jamais
FLOAT — 0.1 + 0.2 ne vaut pas
0.3 en flottant. On peut le démontrer en direct :
SELECT 0.1 + 0.2 = 0.3; -- 0
SELECT CAST(0.1 AS DECIMAL(5,2)) + CAST(0.2 AS DECIMAL(5,2)) = 0.3; -- 1Types chaînes.
VARCHAR(n) : longueur variable, jusqu’à n.
À privilégier pour les textes courts (noms, titres).CHAR(n) : longueur fixe, complétée par des espaces.
Réservé aux codes de longueur constante (code postal, code INSEE) —
rarement utile.TEXT : texte long sans limite pratique raisonnable
(résumés, descriptions). À éviter pour les colonnes indexées ou triées
souvent.Règle pratique : VARCHAR pour tout ce qui est « un nom
», TEXT pour « un paragraphe ».
Types date/heure.
| Type | Usage |
|---|---|
DATE |
date seule (jour) — date_emprunt du projet |
DATETIME |
date + heure, sans fuseau |
TIMESTAMP |
date + heure, avec fuseau — stockage UTC |
TIME |
heure seule |
On mentionne CURRENT_DATE,
CURRENT_TIMESTAMP comme valeurs par défaut fréquentes (on
les verra en séance 5 pour l’insertion).
Types énumérés.
ENUM('disponible','emprunté','perdu') : pratique mais
rigide (ajouter une valeur = ALTER TABLE). On le signale
sans en faire la promotion — préférer une table de référence quand le
domaine peut évoluer.
Choix de type = choix de contrat. Question à poser à
la classe : pourquoi titre est-il
VARCHAR(200) et pas TEXT ? Réponses
attendues : taille bornée, indexable efficacement, comparable
rapidement. Le type n’est pas qu’une question de place, c’est une
contrainte implicite (longueur maximale) qui protège
aussi les données.
NULL : le
piège à connaître (15 min)On commence par une démonstration qui surprend toujours :
SELECT NULL = NULL; -- NULL, pas 1 !
SELECT NULL <> NULL; -- NULL
SELECT 1 = NULL; -- NULL
SELECT NULL IS NULL; -- 1Message : NULL signifie « valeur
inconnue », pas « zéro » ni « chaîne vide ». Toute comparaison avec
NULL renvoie NULL, qui n’est ni vrai
ni faux — donc la ligne n’est pas retenue par
un WHERE. Conséquence pratique :
-- Ne renvoie PAS les livres dont disponible est NULL (s'il y en avait)
SELECT * FROM livre WHERE disponible <> 1;
-- Renvoie aussi les NULL
SELECT * FROM livre WHERE disponible IS NULL OR disponible <> 1;Trois règles à retenir : 1. NOT NULL doit être
le défaut sur les colonnes où une valeur est toujours
attendue (nom d’auteur, titre de livre). 2. NULL est
légitime quand l’information n’existe vraiment pas (une
date_retour non renseignée = emprunt en cours). 3. Dans les
requêtes, toujours se demander : cette colonne peut-elle être
NULL ? Si oui, IS NULL /
IS NOT NULL sont nécessaires.
Mini-exercice oral (2 min) : dans livre, quelle colonne
est nullable ? Quelle colonne devrait l’être mais ne l’est pas
(selon vous) ? Réponse attendue : aucune colonne de livre
n’est nullable ; et pour le projet, emprunt.date_retour
devra l’être.
On reprend le DDL de la séance 1 et on explicite chaque contrainte, en montrant ce qui se passe si on l’enlève.
NOT NULL — déjà vu. On illustre :
titre VARCHAR(200) NOT NULL → insertion sans titre
refusée.
DEFAULT — valeur utilisée si
l’INSERT ne précise pas la colonne.
-- Sans DEFAULT : insertion refusée si on ne donne pas disponible
-- Avec DEFAULT 1 : insertion réussie, disponible = 1
INSERT INTO livre (titre, auteur_id) VALUES ('Test', 1);
SELECT disponible FROM livre WHERE titre = 'Test'; -- 1PRIMARY KEY — identifiant unique et non
nul. On précise : une table a exactement une clé
primaire (éventuellement composite). Elle crée aussi un index.
AUTO_INCREMENT — le SGBD attribue l’id.
On anticipe la séance 5 : LAST_INSERT_ID() pour récupérer
l’id généré.
UNIQUE — unicité sans être clé
primaire. Exemple à donner : dans auteur, on pourrait
vouloir UNIQUE(prenom, nom) pour interdire les doublons. On
le fait en direct :
ALTER TABLE auteur ADD CONSTRAINT uq_auteur_nom UNIQUE (prenom, nom);
-- Tente d'insérer un doublon
INSERT INTO auteur (prenom, nom) VALUES ('George', 'Orwell'); -- refuséCHECK — règle logique sur une ligne.
Attention : MariaDB 10.11 supporte CHECK (contrairement aux
anciennes versions MySQL où il était ignoré). On le vérifie :
ALTER TABLE livre ADD CONSTRAINT ck_livre_titre CHECK (CHAR_LENGTH(titre) > 0);
INSERT INTO livre (titre, auteur_id) VALUES ('', 1); -- refuséFOREIGN KEY — le cœur de la séance. On
reprend les clauses du schéma :
CONSTRAINT fk_livre_auteur
FOREIGN KEY (auteur_id) REFERENCES auteur (id)
ON UPDATE CASCADE
ON DELETE RESTRICTON DELETE RESTRICT : suppression d’un auteur référencé
→ refus (vu en TP1).ON DELETE CASCADE : suppression d’un auteur →
suppression en cascade de ses livres. Dangereux, à
utiliser en connaissance de cause.ON DELETE SET NULL : les livre.auteur_id
deviennent NULL. Nécessite que la colonne soit nullable —
elle ne l’est pas ici, donc cette option serait refusée à la
création.ON UPDATE CASCADE : si on change
auteur.id, la référence suit. Rare mais utile en cas de
fusion d’identifiants.On fait le tour des trois comportements en modifiant une
copie de la base (voir TP), pour ne pas casser le fil rouge.
Message : RESTRICT est le défaut
raisonnable ; CASCADE est un choix explicite qui
doit être justifié par le métier.
Synthèse :
| Contrainte | Ce qu’elle garantit | Exemple dans le schéma | Comportement si violée |
|---|---|---|---|
NOT NULL |
valeur toujours présente | livre.titre |
insertion refusée |
DEFAULT |
valeur par défaut si absente | livre.disponible = 1 |
insertion acceptée avec la valeur par défaut |
PRIMARY KEY |
unicité + non-nullité | auteur.id |
insertion/doublon refusé |
UNIQUE |
unicité | (à ajouter) | doublon refusé |
CHECK |
règle logique | (à ajouter) | insertion/mise à jour refusée |
FOREIGN KEY ... RESTRICT |
existence de la cible | livre.auteur_id |
suppression de l’auteur refusée |
FOREIGN KEY ... CASCADE |
existence + propagation | (variante) | suppression propagée |
On présente ALTER TABLE comme l’outil de toutes les
évolutions. Trois exemples, dans l’ordre de difficulté :
-- 1. Ajouter une colonne (avec valeur par défaut pour les lignes existantes)
ALTER TABLE livre ADD COLUMN annee_publication INT NULL;
-- 2. Ajouter une contrainte
ALTER TABLE auteur ADD CONSTRAINT uq_auteur_nom UNIQUE (prenom, nom);
-- 3. Modifier le type d'une colonne (attention aux données existantes)
ALTER TABLE livre MODIFY COLUMN titre VARCHAR(300) NOT NULL;Message : toute modification de schéma est un acte à
impact, pas une simple édition. Ajouter une colonne
NOT NULL sans DEFAULT sur une
table non vide échoue — on le montre en direct, puis on corrige avec
DEFAULT.
A partir de maintenant, le schéma est stable, on va interroger les données. Ce sera naturellement le but de la séance 3.
Livrable : un fichier
compte_rendu_seance2.txt contenant les réponses numérotées,
les commandes exécutées et les sorties (ou captures). Toutes les
manipulations se font dans une base dédiée
bibliotheque_tp pour ne pas casser le fil rouge.
sudo mariadb --default-character-set=utf8mb4 -u root -p < schema_vide.sqlLe script schema_vide.sql doit contenir :
DROP DATABASE IF EXISTS bibliotheque_tp;
CREATE DATABASE bibliotheque_tp
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
USE bibliotheque_tp;
SET NAMES utf8mb4;
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
CREATE TABLE auteur (
id INT NOT NULL AUTO_INCREMENT,
prenom VARCHAR(100) NOT NULL,
nom VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE livre (
id INT NOT NULL AUTO_INCREMENT,
titre VARCHAR(200) NOT NULL,
auteur_id INT NOT NULL,
disponible TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (id),
KEY idx_livre_auteur (auteur_id),
CONSTRAINT fk_livre_auteur
FOREIGN KEY (auteur_id) REFERENCES auteur (id)
ON UPDATE CASCADE
ON DELETE RESTRICT
) ENGINE=InnoDB;Puis, dans le client :
USE bibliotheque_tp;
SHOW TABLES;Q0 : combien de tables contient
bibliotheque_tp ? Pourquoi est-elle vide contrairement à
bibliotheque ?
a) Précision numérique.
SELECT 0.1 + 0.2 = 0.3; -- ?
SELECT CAST(0.1 AS DECIMAL(5,2)) + CAST(0.2 AS DECIMAL(5,2)) = 0.3; -- ?Q1 : recopiez les deux résultats. Expliquez en 2–3 lignes pourquoi ils diffèrent et quelle conclusion vous en tirez pour le choix d’un type monétaire.
b) Chaînes tronquées.
CREATE TABLE test_types (
id INT PRIMARY KEY AUTO_INCREMENT,
c CHAR(3),
vc VARCHAR(3),
t TEXT
);
INSERT INTO test_types (c, vc, t) VALUES ('abcd', 'abcd', 'abcd');Q2 : que renvoie le
SELECT * FROM test_types; ? Commentez le comportement de
CHAR(3) et de VARCHAR(3) face à une chaîne
trop longue. (Indice : le sql_mode est en
STRICT_TRANS_TABLES — que se serait-il passé sans lui
?)
c) Dates.
CREATE TABLE test_dates (d DATE, dt DATETIME, ts TIMESTAMP);
INSERT INTO test_dates VALUES ('2026-11-09', '2026-11-09 14:30:00', '2026-11-09 14:30:00');
SELECT * FROM test_dates;Q3 : que contient chaque colonne après insertion ?
Quelle est la différence de comportement entre DATETIME et
TIMESTAMP vis-à-vis du fuseau horaire ? (Aide :
SET time_zone = '+02:00'; puis re-SELECT.)
d) Bilan. Q4 : pour chacune des
colonnes suivantes du projet bibliothèque, proposez un type et justifiez
en une phrase : livre.titre, livre.disponible,
livre.annee_publication (à venir),
emprunt.date_emprunt, emprunt.date_retour, un
futur emprunt.amende (montant en euros).
NULL en
pratique (15 min)a) Démonstration.
SELECT NULL = NULL, NULL <> NULL, NULL IS NULL, NULL IS NOT NULL;Q5 : recopiez les quatre résultats. En une phrase,
que signifie « NULL n’est ni vrai ni faux » ?
b) Impact sur les filtres.
-- On rend temporairement une colonne nullable pour l'exercice
ALTER TABLE livre MODIFY COLUMN disponible TINYINT(1) NULL;
INSERT INTO auteur (prenom, nom) VALUES ('A', 'Inconnu');
INSERT INTO livre (titre, auteur_id, disponible) VALUES ('Test NULL', 1, NULL);
-- Requêtes à comparer
SELECT titre FROM livre WHERE disponible <> 1; -- Combien de lignes ?
SELECT titre FROM livre WHERE disponible IS NULL; -- Combien de lignes ?Q6 : combien de lignes renvoie chaque requête ?
Pourquoi Test NULL n’apparaît-il pas dans
la première ? Que faut-il écrire pour le récupérer ?
c) Nettoyage.
DELETE FROM livre WHERE titre = 'Test NULL';
ALTER TABLE livre MODIFY COLUMN disponible TINYINT(1) NOT NULL DEFAULT 1;Q7 : pourquoi la dernière commande
ALTER échouerait-elle si on avait laissé une ligne avec
disponible = NULL ? (Faites le test dans une transaction
que vous annulez, ou expliquez sans exécuter.)
a) DEFAULT.
INSERT INTO auteur (prenom, nom) VALUES ('George', 'Orwell');
INSERT INTO livre (titre, auteur_id) VALUES ('1984', LAST_INSERT_ID());
SELECT titre, disponible FROM livre;Q8 : quelle valeur a disponible pour ce
livre ? Quelle ligne du DDL l’explique ?
b) UNIQUE.
ALTER TABLE auteur ADD CONSTRAINT uq_auteur_nom UNIQUE (prenom, nom);
INSERT INTO auteur (prenom, nom) VALUES ('George', 'Orwell');Q9 : recopiez le message d’erreur. Quelle contrainte
l’a provoqué ? Que se passe-t-il si vous insérez
('George', 'Orwell Jr') ?
c) CHECK.
ALTER TABLE livre ADD CONSTRAINT ck_titre_non_vide CHECK (CHAR_LENGTH(titre) > 0);
INSERT INTO livre (titre, auteur_id) VALUES ('', 1);Q10 : recopiez le message. La contrainte
CHECK est-elle bien appliquée par votre version de MariaDB
? (À vérifier aussi avec SHOW CREATE TABLE livre;.)
d) FOREIGN KEY ... RESTRICT.
DELETE FROM auteur WHERE prenom = 'George' AND nom = 'Orwell';Q11 : recopiez l’erreur. Pourquoi le SGBD refuse-t-il alors qu’il n’existe qu’un seul auteur concerné ?
e) ON DELETE CASCADE — variante, sur une
copie.
CREATE TABLE auteur_bis LIKE auteur;
CREATE TABLE livre_bis LIKE livre;
-- On recrée la FK avec CASCADE sur livre_bis
ALTER TABLE livre_bis DROP FOREIGN KEY fk_livre_auteur;
ALTER TABLE livre_bis
ADD CONSTRAINT fk_livre_bis_auteur
FOREIGN KEY (auteur_id) REFERENCES auteur_bis (id)
ON DELETE CASCADE;
INSERT INTO auteur_bis (prenom, nom) VALUES ('Jules', 'Verne');
INSERT INTO livre_bis (titre, auteur_id) VALUES ('Vingt mille lieues', LAST_INSERT_ID());
DELETE FROM auteur_bis WHERE nom = 'Verne';
SELECT * FROM livre_bis;Q12 : que contient livre_bis après le
DELETE ? Expliquez en une phrase le comportement de
CASCADE et pourquoi il est dangereux par
défaut dans une application métier.
f) Bilan. Q13 : pour le futur
projet, vous devez créer la table
emprunt(livre_id → livre.id, nom_emprunteur, date_emprunt, date_retour NULL).
Quel comportement (RESTRICT, CASCADE,
SET NULL) choisissez-vous pour ON DELETE sur
livre_id ? Justifiez en 3 lignes (pensez à l’historique des
emprunts).
ALTER TABLE sur des données existantes (15 min)a) Ajouter une colonne sans casser l’existant.
ALTER TABLE livre ADD COLUMN annee_publication INT NULL;
SELECT titre, annee_publication FROM livre;Q14 : quelle valeur prennent les lignes existantes ?
Pourquoi NULL ici et pas 0 ?
b) Ajouter une colonne NOT NULL sans
DEFAULT — l’échec.
ALTER TABLE livre ADD COLUMN editeur VARCHAR(100) NOT NULL;Q15 : recopiez l’erreur. Proposez
deux corrections possibles (l’une avec
DEFAULT, l’autre en deux temps : ajout nullable,
remplissage, passage en NOT NULL). Écrivez-les.
c) Renommer / modifier.
ALTER TABLE livre CHANGE annee_publication annee INT NULL;
ALTER TABLE livre MODIFY COLUMN titre VARCHAR(300) NOT NULL;
SHOW CREATE TABLE livre;Q16 : recopiez le
SHOW CREATE TABLE livre final. Listez toutes les
contraintes présentes à ce stade.
Q17 : en 6 à 8 lignes, répondez à : « pourquoi dit-on que le schéma est du code déclaratif qui protège les données ? » Appuyez-vous sur au moins trois contraintes testées pendant ce TP et sur le fait qu’elles s’appliquent indépendamment du langage ou du programme qui écrit dans la base.
| Bloc | Points |
|---|---|
| Q0, préparation | 1 |
| Étape 1 — types (Q1–Q4) | 4 |
| Étape 2 — NULL (Q5–Q7) | 3 |
| Étape 3 — contraintes (Q8–Q13) | 7 |
Étape 4 — ALTER TABLE (Q14–Q16) |
3 |
| Q17 — synthèse | 2 |
schema.sql et repérer toutes les occurrences de
WHERE dans les scripts fournis (data.sql n’en
a pas, mais le TP1 en contenait).SELECT ... WHERE).Question : D’où viennent les refus observés lors du TP de la séance 1 (livre avec auteur inexistant, suppression d’un auteur référencé, auteur sans nom) ? Où ces règles sont-elles écrites ?
Ces règles sont écrites dans le DDL (Data Definition
Language), c’est-à-dire dans le schéma de la
base : ce sont les contraintes déclaratives (FOREIGN KEY,
ON DELETE RESTRICT, NOT NULL). Le schéma est
du code déclaratif : il s’applique à
tous les programmes qui utilisent la base,
contrairement à un contrôle écrit dans le code applicatif qui ne protège
que l’application qui l’exécute.
Question : Objectif 1 — Pourquoi choisit-on
VARCHAR(200) plutôt que TEXT pour une colonne
comme livre.titre ? Citez au moins deux raisons.
VARCHAR(200)
impose une limite maximale, ce qui constitue une contrainte
implicite qui protège les données (on ne peut pas y stocker
n’importe quoi).VARCHAR est indexable et comparable
efficacement ; TEXT est à éviter pour les colonnes
souvent indexées ou triées. Règle pratique : VARCHAR pour «
un nom », TEXT pour « un paragraphe ».Question : Objectif 1 — Quel type choisir pour
stocker un montant en euros (futur
emprunt.amende) ? Et pourquoi ne surtout pas utiliser
FLOAT ?
On choisit DECIMAL(p,s) (par exemple
DECIMAL(8,2)), qui est un type exact. Il
ne faut jamais utiliser
FLOAT/DOUBLE pour de l’argent, car ce sont des
types à approximation binaire : 0.1 + 0.2
ne vaut pas exactement 0.3 en flottant (la comparaison
SELECT 0.1 + 0.2 = 0.3; renvoie 0, tandis qu’en
DECIMAL elle renvoie 1).
Question : Objectif 1 — Quelle est la différence
entre CHAR(n) et VARCHAR(n) ? Pour quel usage
réserver CHAR(n) ?
CHAR(n) : longueur
fixe, complétée par des espaces si la valeur est plus
courte. Réservé aux codes de longueur constante (code postal, code
INSEE), rarement utile.VARCHAR(n) : longueur
variable, jusqu’à n. À privilégier pour
les textes courts (noms, titres).Question : Objectif 1 — Distinguez les types
DATE, DATETIME et TIMESTAMP.
DATE : date seule (jour), par exemple
date_emprunt.DATETIME : date + heure,
sans fuseau horaire.TIMESTAMP : date + heure,
avec fuseau horaire (stockage en UTC) ; la valeur
affichée peut donc changer si l’on change de fuseau. À quoi s’ajoute
TIME pour une heure seule.Question : Objectif 1 — Que se passe-t-il si l’on
insère 'abcd' dans une colonne VARCHAR(3) avec
sql_mode = STRICT_TRANS_TABLES ? Et sans ce mode ?
Avec STRICT_TRANS_TABLES, l’insertion est
refusée (erreur « Data too long for column ») : le SGBD
protège l’intégrité. Sans ce mode (mode non strict), le SGBD
tronque silencieusement la valeur à 3 caractères
('abc'), ce qui est une perte de données silencieuse — d’où
l’intérêt du mode strict.
Question : Objectif 4 — Que renvoie
SELECT NULL = NULL; ? Et SELECT NULL IS NULL;
? Que faut-il en conclure sur la nature de NULL ?
SELECT NULL = NULL; renvoie
NULL (et non 1).SELECT NULL IS NULL; renvoie
1 (vrai). NULL signifie «
valeur inconnue », pas « zéro » ni « chaîne vide ».
Toute comparaison avec NULL (=,
<>, <, …) renvoie NULL,
qui n’est ni vrai ni faux. Seuls les opérateurs
IS NULL et IS NOT NULL permettent de tester la
nullité.Question : Objectif 4 — Pourquoi la requête
SELECT titre FROM livre WHERE disponible <> 1; ne
renvoie-t-elle pas une ligne dont
disponible vaut NULL ? Comment récupérer cette
ligne ?
Parce que la comparaison NULL <> 1 renvoie
NULL (ni vrai ni faux), donc la ligne
n’est pas retenue par le WHERE. Pour
récupérer aussi les lignes NULL, il faut écrire
explicitement :
WHERE disponible IS NULL OR disponible <> 1;.
Question : Objectif 4 — Quand NULL
est-il légitime dans un schéma ? Donnez l’exemple du
projet bibliothèque.
NULL est légitime quand l’information n’existe
vraiment pas (et non pas « vaut zéro » ou « est vide »).
Exemple du projet : emprunt.date_retour
doit être nullable — une valeur NULL
signifie « emprunt en cours, pas encore rendu ». En revanche,
NOT NULL doit être le défaut sur les
colonnes où une valeur est toujours attendue (nom d’auteur, titre de
livre).
Question : Objectif 4 — Pourquoi la commande
ALTER TABLE livre MODIFY COLUMN disponible TINYINT(1) NOT NULL DEFAULT 1;
échouerait-elle si une ligne avait encore disponible = NULL
?
Parce que le passage en NOT NULL
interdit toute valeur nulle : le SGBD refuse
d’appliquer la modification tant qu’une ligne viole la nouvelle
contrainte (erreur du type « Column ‘disponible’ cannot be null » / «
Invalid use of NULL value »). Il faut d’abord traiter
les lignes concernées (les supprimer ou remplacer leur NULL
par une valeur), puis modifier la colonne.
Question : Objectif 2 — Complétez le rôle de chaque
contrainte : NOT NULL, DEFAULT,
PRIMARY KEY, UNIQUE, CHECK,
FOREIGN KEY.
| Contrainte | Ce qu’elle garantit |
|---|---|
NOT NULL |
valeur toujours présente (pas de NULL) |
DEFAULT |
valeur utilisée si l’INSERT ne précise pas la
colonne |
PRIMARY KEY |
unicité et non-nullité ; une seule par table (éventuellement composite) |
UNIQUE |
unicité sans être clé primaire |
CHECK |
règle logique sur une ligne (ex.
CHAR_LENGTH(titre) > 0) |
FOREIGN KEY |
existence de la cible référencée (intégrité référentielle) |
Question : Objectif 2 — Combien de clés primaires
une table peut-elle avoir ? Que se passe-t-il si l’on tente d’insérer un
doublon sur une colonne PRIMARY KEY ou UNIQUE
?
Une table a exactement une clé primaire (elle peut
être composite, c’est-à-dire portée par plusieurs
colonnes ; en théorie mais ce cas est très rare car il est moins
pratique). Une tentative d’insertion d’un doublon sur
une colonne PRIMARY KEY ou sur une contrainte
UNIQUE est refusée par le SGBD (erreur «
Duplicate entry »).
Question : Objectif 2 — Que garantit une contrainte
CHECK ? La contrainte CHECK est-elle
réellement appliquée par MariaDB 10.11 ? Comment le vérifier ?
CHECK garantit qu’une règle logique est
respectée sur chaque ligne (ex.
CHECK (CHAR_LENGTH(titre) > 0) refuse un titre vide).
Contrairement aux anciennes versions de MySQL où elle était ignorée,
MariaDB 10.11 applique réellement CHECK :
l’insertion d’un titre vide est refusée. On le vérifie en consultant
SHOW CREATE TABLE livre; (la contrainte y figure) et en
testant une insertion invalide (refus).
Question : Objectif 2 — Expliquez les trois
comportements possibles de ON DELETE sur une clé étrangère
: RESTRICT, CASCADE, SET NULL.
Lequel est le « défaut raisonnable » et pourquoi ?
RESTRICT : la suppression de la ligne
référencée est refusée tant qu’une
ligne la référence (ex. livre.auteur_id).CASCADE : la suppression est
propagée aux lignes qui référencent (supprimer un
auteur supprime ses livres) — dangereux, car une simple suppression peut
en entraîner beaucoup d’autres.SET NULL : les colonnes référentes
passent à NULL (nécessite que la colonne
soit nullable ; sinon la contrainte est refusée à la création). Le
défaut raisonnable est
RESTRICT : CASCADE doit être
un choix explicite justifié par le métier, pas un
comportement par défaut.Question : Objectif 2 — Dans le TP, vous avez testé
ON DELETE CASCADE sur livre_bis. Que contient
livre_bis après suppression de l’auteur « Verne » ?
Pourquoi ce comportement est-il dangereux dans une application métier
?
Après le DELETE de l’auteur, la ligne de
livre_bis qui le référençait a été supprimée
automatiquement (la table est vide). C’est dangereux car
une seule suppression peut en entraîner
beaucoup d’autres en chaîne, sans avertissement, et
potentiellement détruire un historique ou des données précieuses. C’est
pourquoi CASCADE doit être un choix explicite et
justifié.
Question : Objectif 2 — Que se passe-t-il si l’on
supprime un auteur référencé par des livres avec la contrainte
ON DELETE RESTRICT du schéma fourni ? Comment procéder pour
le supprimer légitimement ?
La suppression est refusée par le SGBD (erreur de
contrainte de clé étrangère), car des lignes de livre
référencent encore cet auteur. Pour le supprimer légitimement, il faut
d’abord traiter ses livres : soit les supprimer, soit
modifier leur auteur_id pour les réaffecter à un autre
auteur, de sorte qu’aucune ligne ne référence plus l’auteur visé.
Question : Objectif 2 —
ON DELETE SET NULL serait-il accepté sur
livre.auteur_id dans le schéma fourni ? Justifiez.
Non : livre.auteur_id est déclarée
NOT NULL, or SET NULL exige que la colonne
référente soit nullable. Le SGBD refuserait la création
de cette contrainte. Pour pouvoir utiliser SET NULL, il
faudrait d’abord rendre auteur_id nullable (ce qui
changerait le contrat du schéma).
Question : Objectif 2 — Quelle est la différence
entre ON DELETE et ON UPDATE sur une clé
étrangère ? Que fait ON UPDATE CASCADE ?
ON DELETE définit le comportement
quand la ligne référencée est
supprimée.ON UPDATE définit le comportement
quand la valeur de la clé de la ligne référencée est
modifiée.
ON UPDATE CASCADE : si l’on modifie la
valeur de auteur.id, la modification est propagée
automatiquement aux livre.auteur_id correspondants
(utile par exemple en cas de fusion d’identifiants).Question : Objectif 3 — Qu’est-ce
qu’ALTER TABLE ? Citez les trois opérations vues en
cours/TP (avec la commande correspondante).
ALTER TABLE est l’outil de modification du
schéma d’une table existante sans perdre les
données. Trois opérations vues :
ALTER TABLE livre ADD COLUMN annee_publication INT NULL;ALTER TABLE auteur ADD CONSTRAINT uq_auteur_nom UNIQUE (prenom, nom);ALTER TABLE livre MODIFY COLUMN titre VARCHAR(300) NOT NULL;
(on a aussi vu CHANGE pour renommer une colonne).Question : Objectif 3 — Lorsque l’on ajoute une
colonne annee_publication INT NULL à une table
livre déjà remplie, quelle valeur prennent les lignes
existantes ? Pourquoi NULL et pas 0 ?
Les lignes existantes prennent la valeur
NULL. On obtient NULL et non
0 parce que 0 serait une valeur
inventée (une année de publication nulle n’a pas de sens),
alors que NULL signifie correctement «
inconnue ». C’est le comportement attendu : ne pas
fabriquer de fausse donnée.
Question : Objectif 3 — Que se passe-t-il si l’on
exécute
ALTER TABLE livre ADD COLUMN editeur VARCHAR(100) NOT NULL;
sur une table non vide ? Proposez deux corrections
possibles.
L’opération échoue : ajouter une colonne
NOT NULL sans DEFAULT sur une
table contenant déjà des lignes est impossible, car les lignes
existantes n’auraient aucune valeur valide. Deux corrections :
DEFAULT :
ALTER TABLE livre ADD COLUMN editeur VARCHAR(100) NOT NULL DEFAULT 'inconnu';ALTER TABLE livre ADD COLUMN editeur VARCHAR(100) NULL;
puis remplir les lignes (UPDATE), puis
ALTER TABLE livre MODIFY COLUMN editeur VARCHAR(100) NOT NULL;.Question : Objectif 3 — Pourquoi dit-on que « toute modification de schéma est un acte à impact » ? Illustrez avec un exemple du TP.
Parce que modifier le schéma ne consiste pas à éditer un simple
fichier : cela affecte les données existantes et peut
échouer ou perdre des informations si
l’on ne prend pas de précautions. Exemple du TP : ajouter une colonne
NOT NULL sans DEFAULT sur une table non vide
échoue ; modifier le type d’une colonne
(titre en VARCHAR(300)) doit tenir compte des
données déjà présentes. Il faut donc anticiper l’impact avant d’exécuter
l’ALTER.
Question : Objectif 3 — Quelles contraintes
trouve-t-on dans le SHOW CREATE TABLE livre final du TP ?
Listez-les.
Réponse attendue (selon l’ordre des manipulations du TP) : la
clé primaire sur id, la clé
étrangère fk_livre_auteur
(auteur_id → auteur.id, ON UPDATE CASCADE,
ON DELETE RESTRICT), l’index
idx_livre_auteur sur auteur_id, le
NOT NULL et
DEFAULT 1 sur disponible, le
NOT NULL sur titre (type
modifié en VARCHAR(300)), la contrainte
CHECK ck_titre_non_vide, et
la colonne annee (ex-annee_publication) en
INT NULL.
Question : Synthèse — Pourquoi dit-on que le schéma est du « code déclaratif qui protège les données » ? Appuyez-vous sur au moins trois contraintes testées pendant ce TP.
Réponse attendue (6 à 8 lignes) articulant au moins trois contraintes parmi :
FOREIGN KEY ... RESTRICT : empêche de
supprimer un auteur encore référencé par des livres (intégrité
référentielle).UNIQUE : empêche l’insertion d’un
doublon d’auteur (('George', 'Orwell') refusé une seconde
fois).CHECK : empêche d’insérer un titre
vide.NOT NULL : empêche une valeur absente
(auteur sans nom, refus vu en séance 1).DEFAULT : garantit une valeur
cohérente quand l’INSERT l’omet
(disponible = 1).Argument clé : ces contraintes sont déclarées une fois dans le schéma et appliquées par le SGBD à tous les programmes, quel que soit leur langage — contrairement à un contrôle applicatif qui ne protège qu’une seule application.
Question : Question ouverte de préparation (séance 3) : comment formuleriez-vous en français le besoin « tous les livres de Jules Verne », sans écrire la requête SQL ?
Réponse attendue (amorce, sans exigence SQL) : « Je veux la liste des
livres dont l’auteur s’appelle Jules Verne », c’est-à-dire
filtrer les lignes de la table livre selon
une condition sur l’auteur. Cette formulation prépare la séance 3
(SELECT ... FROM ... WHERE) : on part du besoin
métier formulé en français pour aboutir à la requête. Aucune
écriture SQL n’est attendue ici.