Chapitre 323 minDébutant
CREATE TABLE, clés et contraintes (LDD)
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- créer une base et ses tables avec
CREATE TABLE; - choisir le bon type pour chaque colonne ;
- poser les contraintes : clé primaire, clé étrangère,
UNIQUE,CHECK; - choisir l'action
ON DELETEd'une clé étrangère ; - faire évoluer une table avec
ALTER TABLE.
5 exercices corrigés
CREATE TABLE crée une table avec ses colonnes, leurs types et leurs contraintes : clé primaire, clé étrangère, NOT NULL, UNIQUE, CHECK. Avec ALTER TABLE et DROP, ces instructions forment le LDD, le langage de définition des données. Les exemples modifient la base d'exemple parc, comme ceux du chapitre INSERT, UPDATE et DELETE. Pour retrouver exactement les résultats du cours, rejoue son script avant chaque série d'exercices : il remet la base dans son état initial.
CREATE DATABASE, jeu de caractères et interclassement
-- La base parc existe déjà (script de la base d'exemple) : IF NOT EXISTS évite l'erreur 1007
CREATE DATABASE IF NOT EXISTS parc CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE parc;
Le jeu de caractères (CHARACTER SET) dit comment les caractères sont codés en octets. Choisis toujours utf8mb4 : c'est le vrai UTF-8, qui code chaque caractère sur un à quatre octets et accepte toutes les langues et les émojis. L'ancien utf8 de MySQL (renommé utf8mb3) s'arrête à trois octets et il est obsolète.
L'interclassement (COLLATE) dit comment comparer et trier les chaînes. utf8mb4_0900_ai_ci, l'interclassement par défaut de MySQL 8, est insensible aux accents (ai, accent insensitive) et à la casse (ci, case insensitive). Conséquence directe :
SELECT id_employe, nom FROM employe WHERE nom = 'LEFEVRE';
| id_employe | nom |
|---|---|
| 3 | Lefèvre |
Pour une comparaison stricte, il existe utf8mb4_0900_as_cs (sensible aux accents et à la casse) et utf8mb4_bin (comparaison octet par octet).
CREATE DATABASE IF NOT EXISTS parc ne fait rien si la base existe déjà ; DROP DATABASE parc la supprime avec toutes ses tables. Pour MySQL, DATABASE et SCHEMA sont synonymes.
La syntaxe de CREATE TABLE
-- Syntaxe générale (les crochets marquent les parties facultatives)
CREATE TABLE [IF NOT EXISTS] nom_table (
colonne TYPE [NOT NULL] [DEFAULT valeur] [AUTO_INCREMENT] [UNIQUE] [PRIMARY KEY],
...,
[CONSTRAINT nom] PRIMARY KEY (colonne, ...),
[CONSTRAINT nom] UNIQUE (colonne, ...),
[CONSTRAINT nom] CHECK (condition),
[CONSTRAINT nom] FOREIGN KEY (colonne) REFERENCES table_parent (colonne)
[ON DELETE action] [ON UPDATE action]
);
Une contrainte s'écrit à deux endroits. En ligne, à la suite de la colonne (email VARCHAR(100) NOT NULL UNIQUE) : c'est court, mais seulement pour une contrainte sur une seule colonne. En fin de table, après les colonnes (PRIMARY KEY (id_poste, id_logiciel)) : obligatoire pour une contrainte sur plusieurs colonnes, et c'est là qu'on lui donne un nom avec CONSTRAINT.
Nomme tes contraintes. Sans nom, MySQL en invente un (employe_ibfk_1, poste_chk_2) et tes messages d'erreur deviennent illisibles. La base parc suit une convention simple : fk_table_cible pour une clé étrangère, ck_table_sujet pour un CHECK.
Le service informatique veut maintenant suivre ses fournisseurs de matériel. Voici la table, commentée :
CREATE TABLE fournisseur (
id_fournisseur INT AUTO_INCREMENT PRIMARY KEY, -- clé artificielle
raison_sociale VARCHAR(100) NOT NULL,
siret CHAR(14) NOT NULL, -- toujours 14 chiffres
email VARCHAR(100), -- facultatif
delai_jours TINYINT NOT NULL DEFAULT 5, -- délai de livraison habituel
CONSTRAINT uq_fournisseur_siret UNIQUE (siret),
CONSTRAINT ck_fournisseur_siret CHECK (siret REGEXP '^[0-9]{14}$'),
CONSTRAINT ck_fournisseur_delai CHECK (delai_jours BETWEEN 1 AND 60)
);
Choisir les types
Un type fixe ce qu'une colonne accepte et la place qu'elle occupe. Le bon type est la première contrainte d'intégrité.
Nombres entiers
| Type | Octets | Plage signée | Plage UNSIGNED | Exemple |
|---|---|---|---|---|
TINYINT | 1 | −128 à 127 | 0 à 255 | Étage, note, booléen |
SMALLINT | 2 | −32 768 à 32 767 | 0 à 65 535 | Durée en minutes, nombre de licences |
MEDIUMINT | 3 | −8 388 608 à 8 388 607 | 0 à 16 777 215 | Rare |
INT | 4 | environ ±2,1 milliards | 0 à 4 294 967 295 | Clés primaires, quantités |
BIGINT | 8 | environ ±9,2 × 10¹⁸ | 0 à environ 1,8 × 10¹⁹ | Identifiants de très grosses tables, journaux |
INT(11) ne limite rien : le nombre entre parenthèses était une largeur d'affichage, obsolète depuis MySQL 8.0.17. Écris INT.
Nombres décimaux
| Type | Précision | Usage |
|---|---|---|
DECIMAL(p, s) | Exacte : p chiffres au total, dont s après la virgule | Montants, prix, quantités mesurées. DECIMAL(8,2) va jusqu'à 999 999,99 |
FLOAT, DOUBLE | Approchée (virgule flottante binaire) | Mesures scientifiques, coordonnées |
SELECT 0.1e0 + 0.2e0 AS en_double, 0.1 + 0.2 AS en_decimal;
| en_double | en_decimal |
|---|---|
| 0.30000000000000004 | 0.3 |
Écrit avec un exposant, 0.1e0 est un DOUBLE : la somme n'est pas exactement 0,3. Écrit sans exposant, 0.1 est un DECIMAL exact.
Chaînes
| Type | Taille | Usage |
|---|---|---|
CHAR(n) | Toujours n caractères (complétée par des espaces, retirés à la lecture) | Valeurs de longueur fixe : num_inventaire, code postal, SIRET |
VARCHAR(n) | Jusqu'à n caractères, plus 1 ou 2 octets de longueur | Noms, emails, titres. Une ligne entière ne peut pas dépasser 65 535 octets |
TEXT | Jusqu'à 65 535 octets | Descriptions longues |
MEDIUMTEXT, LONGTEXT | 16 Mio, 4 Gio | Contenus volumineux, rarement utile |
Dans VARCHAR(50), 50 est un nombre de caractères, pas d'octets. CHAR(n) fixe la place réservée, pas la longueur saisie : 'PC-1' entre dans le CHAR(7) de num_inventaire, complété par des espaces. Pour imposer une longueur exacte, il faut un CHECK, comme celui du SIRET de la table fournisseur.
Dates et heures
| Type | Format | Plage | Usage |
|---|---|---|---|
DATE | '2026-10-28' | Années 1000 à 9999 | Date d'achat, d'embauche |
DATETIME | '2026-10-28 14:30:00' | Années 1000 à 9999 | Date et heure d'un évènement métier |
TIMESTAMP | '2026-10-28 14:30:00' | 1970-01-01 à 2038-01-19 (UTC) | Horodatage technique, converti selon le fuseau de la session |
TIME | '14:30:00' | ±838 heures | Durée, heure seule |
YEAR | 2026 | 1901 à 2155 | Année seule |
Les dates s'écrivent toujours 'AAAA-MM-JJ', entre apostrophes, quel que soit l'affichage de ton client.
Autres types utiles
BOOLEAN: synonyme deTINYINT(1).TRUEvaut 1 etFALSEvaut 0.ENUM('basse', 'normale', 'haute'): une valeur parmi une liste, propre à MySQL et MariaDB. Le tri suit l'ordre de déclaration, pas l'ordre alphabétique. Ajouter une valeur impose unALTER TABLE: la baseparcpréfèreVARCHARavec unCHECK, une écriture portable.JSON: un document JSON validé à l'insertion (les colonnes JSON dans MySQL).BINARY(n),VARBINARY(n),BLOB: des octets bruts, par exemple une donnée chiffrée (chiffrer une colonne sensible).
Les types varient d'un SGBD à l'autre. PostgreSQL n'a ni TINYINT, ni UNSIGNED, ni ENUM en ligne : utilise SMALLINT, un CHECK, ou CREATE TYPE ... AS ENUM. BOOLEAN y est un vrai type, et l'auto-incrément s'écrit GENERATED BY DEFAULT AS IDENTITY (ou ALWAYS pour refuser les valeurs saisies) ; après des insertions avec identifiants explicites, comme dans le script de la base parc, recale la séquence avec setval. SQL Server utilise INT IDENTITY(1,1), BIT pour les booléens, NVARCHAR pour l'Unicode et DATETIME2 pour les dates.
Les contraintes
PRIMARY KEY. Unique et non nulle. Sur une colonne, en ligne ; sur plusieurs, en fin de table : PRIMARY KEY (id_poste, id_logiciel).
AUTO_INCREMENT. MySQL attribue la valeur suivante quand tu n'en fournis pas. Une valeur supprimée n'est pas réutilisée, et depuis MySQL 8.0 le compteur survit au redémarrage du serveur. Une seule colonne par table peut l'avoir, et elle doit être indexée (en pratique, c'est la clé primaire).
NOT NULL. La colonne est obligatoire. Sans NOT NULL, elle accepte NULL.
DEFAULT. La valeur utilisée quand l'INSERT ne cite pas la colonne : une constante (DEFAULT 'normale'), CURRENT_TIMESTAMP pour un DATETIME, ou depuis MySQL 8.0.13 une expression entre parenthèses (DEFAULT (CURRENT_DATE)).
UNIQUE. Interdit les doublons. Elle accepte plusieurs NULL, car deux valeurs absentes ne sont pas considérées comme égales. Une contrainte UNIQUE (nom, prenom) porte sur le couple.
CHECK. Une condition que chaque ligne doit respecter. MySQL l'applique depuis la version 8.0.16 ; avant, il l'acceptait dans la syntaxe et l'ignorait. MariaDB l'applique depuis la 10.2.1. La condition ne peut utiliser que des colonnes de la ligne et des fonctions déterministes : ni sous-requête, ni NOW(), ni CURRENT_DATE.
FOREIGN KEY. La colonne doit avoir un type compatible avec la clé référencée : même type entier et même signe (un INT ne peut pas référencer un INT UNSIGNED), même jeu de caractères et même interclassement pour une chaîne (la longueur, elle, peut différer). La colonne référencée doit être une clé primaire ou porter une contrainte UNIQUE. La table parente doit exister avant.
Clé étrangère : ON DELETE et ON UPDATE
Que faire des lignes enfants quand on supprime, ou qu'on modifie la clé de, la ligne parente qu'elles référencent ?
| Action | Effet | Exemple dans la base parc |
|---|---|---|
RESTRICT ou NO ACTION (par défaut) | Refuse l'opération tant que des lignes enfants existent | On ne supprime pas un service qui a des employés |
CASCADE | Supprime (ou met à jour) les lignes enfants avec la ligne parente | Supprimer un ticket supprime ses interventions |
SET NULL | Met la clé étrangère des lignes enfants à NULL (la colonne doit accepter NULL) | Supprimer un employé remet ses postes en stock |
SET DEFAULT | Reconnu par l'analyseur, mais refusé par InnoDB | À ne pas utiliser |
Chaque choix de la base fil rouge traduit une règle métier :
| Clé étrangère | ON DELETE | Règle métier |
|---|---|---|
employe.id_service | RESTRICT | Un service qui a encore des employés ne disparaît pas |
employe.id_responsable | SET NULL | Si un responsable part, son équipe reste, sans responsable désigné |
poste.id_employe | SET NULL | Le poste d'un employé supprimé retourne en stock |
installation.id_poste | CASCADE | Un poste mis au rebut emporte la liste de ses logiciels |
installation.id_logiciel | RESTRICT | On ne supprime pas un logiciel encore installé |
ticket.id_demandeur | RESTRICT | On garde l'historique : un employé qui a ouvert des tickets n'est pas supprimé |
ticket.id_poste | SET NULL | Le ticket survit au poste, sans référence |
intervention.id_ticket | CASCADE | Une intervention n'a pas de sens sans son ticket |
intervention.id_technicien | RESTRICT | On garde la trace de qui est intervenu |
Essaie :
DELETE FROM ticket WHERE id_ticket = 9; -- le ticket 9 avait 3 interventions
SELECT COUNT(*) FROM intervention; -- 17 - 3
| COUNT(*) |
|---|
| 14 |
DELETE FROM service WHERE id_service = 5;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails
(`parc`.`employe`, CONSTRAINT `fk_employe_service` FOREIGN KEY (`id_service`) REFERENCES `service` (`id_service`))
Le message cite la table enfant (employe) et le nom de la contrainte : c'est pour ça qu'on les nomme. Le service 6, Juridique, n'a aucun employé : DELETE FROM service WHERE id_service = 6; fonctionne.
ON UPDATE CASCADE répercute un changement de valeur de la clé primaire. Avec des clés artificielles, qui ne changent jamais, il sert rarement.
Le moteur InnoDB
MySQL stocke chaque table avec un moteur de stockage. Le moteur par défaut, depuis MySQL 5.5, est InnoDB : c'est lui qui gère les transactions, les clés étrangères, les verrous par ligne et la reprise après une panne. L'ancien moteur MyISAM ne gère ni transactions ni clés étrangères.
SELECT table_name AS nom_table, engine AS moteur
FROM information_schema.tables
WHERE table_schema = 'parc';
Méfie-toi des vieux tutoriels qui écrivent ENGINE = MyISAM : tu perds les clés étrangères sans erreur, car MyISAM lit la clause FOREIGN KEY et l'ignore. Laisse le moteur par défaut, ou écris ENGINE = InnoDB.
ALTER TABLE
ALTER TABLE modifie une table existante sans perdre ses données.
-- Ajouter une colonne : elle vaut NULL (ou sa valeur DEFAULT) dans les lignes existantes
ALTER TABLE poste ADD COLUMN fin_garantie DATE;
-- Ajouter une contrainte
ALTER TABLE poste ADD CONSTRAINT ck_poste_garantie CHECK (fin_garantie >= date_achat);
-- Changer le type ou les options d'une colonne : redonne la définition complète
ALTER TABLE poste MODIFY COLUMN marque VARCHAR(40) NOT NULL;
-- Renommer une colonne (MySQL 8.0 et MariaDB 10.5 et plus)
ALTER TABLE poste RENAME COLUMN modele TO modele_constructeur;
-- Changer la valeur par défaut seulement
ALTER TABLE ticket ALTER COLUMN priorite SET DEFAULT 'basse';
-- Supprimer une contrainte CHECK, puis la colonne
ALTER TABLE poste DROP CHECK ck_poste_garantie;
ALTER TABLE poste DROP COLUMN fin_garantie;
Pour relier les postes à la table fournisseur créée avec CREATE TABLE, ajoute la colonne et la clé étrangère en une seule instruction :
ALTER TABLE poste
ADD COLUMN id_fournisseur INT,
ADD CONSTRAINT fk_poste_fournisseur FOREIGN KEY (id_fournisseur)
REFERENCES fournisseur (id_fournisseur);
Pour supprimer une clé étrangère, MySQL demande son nom : ALTER TABLE poste DROP FOREIGN KEY fk_poste_fournisseur;. L'index créé avec elle reste en place ; ALTER TABLE poste DROP INDEX fk_poste_fournisseur; le supprime. Pour renommer une table : RENAME TABLE fournisseur TO prestataire;.
Ailleurs, la syntaxe change. MariaDB supprime une contrainte CHECK avec DROP CONSTRAINT nom, que MySQL accepte aussi depuis la 8.0.19. PostgreSQL n'a pas MODIFY : il écrit ALTER TABLE poste ALTER COLUMN marque TYPE VARCHAR(40) puis ALTER COLUMN marque SET NOT NULL. SQL Server écrit ALTER COLUMN et renomme avec la procédure sp_rename.
DROP, TRUNCATE et DELETE
DELETE FROM t | TRUNCATE TABLE t | DROP TABLE t | |
|---|---|---|---|
| Famille | LMD | LDD | LDD |
| Supprime | Les lignes choisies par le WHERE (toutes sans WHERE) | Toutes les lignes | Les lignes et la structure |
| Peut s'annuler dans une transaction | Oui | Non (validation implicite) | Non (validation implicite) |
Déclenche les triggers DELETE | Oui | Non | Non |
Compteur AUTO_INCREMENT | Conservé | Remis à zéro | Supprimé avec la table |
| Table référencée par une clé étrangère | Vérifie chaque ligne | Refusé | Refusé |
Les tables liées se créent des parents vers les enfants et se suppriment dans l'ordre inverse. Pour la base parc :
DROP TABLE IF EXISTS intervention;
DROP TABLE IF EXISTS ticket;
DROP TABLE IF EXISTS installation;
DROP TABLE IF EXISTS poste;
DROP TABLE IF EXISTS logiciel;
DROP TABLE IF EXISTS employe;
DROP TABLE IF EXISTS service;
Tu verras parfois SET FOREIGN_KEY_CHECKS = 0; avant une suppression ou un import. Cette commande coupe la vérification des clés étrangères pour ta session : tout ce que tu insères pendant ce temps n'est pas contrôlé, et MySQL ne revérifie rien quand tu la remets à 1. Réserve-la aux restaurations de sauvegardes, jamais pour contourner une erreur que tu ne comprends pas.
Repérer une table mal conçue
Avant d'écrire un CREATE TABLE, vérifie que la table respecte les trois premières formes normales. Le cours Merise les présente en détail ; voici comment les repérer.
1re forme normale : des valeurs atomiques. Chaque cellule contient une seule valeur. Une colonne logiciels qui contient 'Firefox, Odoo, Sage' viole la 1FN : impossible de compter les installations d'Odoo ou d'interdire une faute de frappe. La solution est la table installation, une ligne par couple poste-logiciel.
2e forme normale : toute la clé. Dans une table à clé composée, chaque colonne non clé dépend de la clé entière. Une table installation (id_poste, id_logiciel, date_installation, nom_logiciel) viole la 2FN : nom_logiciel ne dépend que de id_logiciel. Sa place est dans logiciel.
3e forme normale : rien que la clé. Une colonne non clé ne dépend pas d'une autre colonne non clé. Une table employe (..., id_service, libelle_service) viole la 3FN : libelle_service dépend de id_service, pas de l'employé. Si le service change de nom, toutes ses lignes sont à modifier.
Exercices
Exercice 1 sur 5 Créer une table avec ses contraintes Réussi À revoir
Écris le CREATE TABLE d'une table contrat_maintenance :
- un identifiant auto-incrémenté ;
- une référence de contrat obligatoire et unique, de 12 caractères exactement ;
- le fournisseur (clé étrangère vers
fournisseur, obligatoire) ; - une date de début et une date de fin, obligatoires, la fin étant postérieure au début ;
- un montant annuel obligatoire, positif ou nul, jusqu'à 99 999,99 €.
Nomme toutes les contraintes de table.
Voir la réponseMasquer la réponse de l'exercice 1
CREATE TABLE contrat_maintenance (
id_contrat INT AUTO_INCREMENT PRIMARY KEY,
reference CHAR(12) NOT NULL,
id_fournisseur INT NOT NULL,
date_debut DATE NOT NULL,
date_fin DATE NOT NULL,
montant_annuel DECIMAL(7,2) NOT NULL,
CONSTRAINT uq_contrat_reference UNIQUE (reference),
CONSTRAINT ck_contrat_reference CHECK (CHAR_LENGTH(reference) = 12),
CONSTRAINT ck_contrat_dates CHECK (date_fin > date_debut),
CONSTRAINT ck_contrat_montant CHECK (montant_annuel >= 0),
CONSTRAINT fk_contrat_fournisseur FOREIGN KEY (id_fournisseur)
REFERENCES fournisseur (id_fournisseur)
);
CHAR(12) fixe la place, pas la longueur saisie : une référence plus courte serait complétée par des espaces et acceptée. Seul le CHECK impose exactement 12 caractères. DECIMAL(7,2) : 7 chiffres dont 2 après la virgule, donc 99 999,99 au maximum. La table fournisseur doit exister avant celle-ci.
Tu avais trouvé ?
Exercice 2 sur 5 Choisir le bon type Réussi À revoir
Propose un type MySQL pour chaque donnée : un prix d'achat, un code postal, une adresse email, un mot de passe haché avec password_hash() en PHP, le nombre de pièces d'un colis (moins de 1 000), un indicateur « compte actif », la date et l'heure d'une connexion, un commentaire libre de plusieurs paragraphes.
Voir la réponseMasquer la réponse de l'exercice 2
| Donnée | Type | Pourquoi |
|---|---|---|
| Prix d'achat | DECIMAL(8,2) | Valeur exacte, jamais FLOAT |
| Code postal | CHAR(5) | Zéro initial (01000), aucun calcul |
VARCHAR(100) à VARCHAR(254) | Longueur variable | |
| Mot de passe haché | VARCHAR(255) | La documentation PHP recommande 255 caractères pour que la colonne suive l'évolution des algorithmes |
| Nombre de pièces | SMALLINT UNSIGNED | Entier positif, 2 octets suffisent |
| Compte actif | BOOLEAN | 0 ou 1 |
| Date de connexion | DATETIME | Date et heure ; TIMESTAMP convient aussi pour un horodatage technique |
| Commentaire long | TEXT | Peut dépasser quelques centaines de caractères |
Tu avais trouvé ?
Exercice 3 sur 5 Faire évoluer la table logiciel Réussi À revoir
- Ajoute à
logicielune colonne facultativefin_support(la date de fin de support de l'éditeur). - Ajoute une colonne
cout_annuel, obligatoire, de 0 par défaut, avec au plus 9 chiffres dont 2 décimales, et une contrainte qui interdit les valeurs négatives. - Quelles valeurs prennent ces deux colonnes dans les 8 lignes existantes ?
Voir la réponseMasquer la réponse de l'exercice 3
ALTER TABLE logiciel ADD COLUMN fin_support DATE;
ALTER TABLE logiciel
ADD COLUMN cout_annuel DECIMAL(9,2) NOT NULL DEFAULT 0,
ADD CONSTRAINT ck_logiciel_cout CHECK (cout_annuel >= 0);
SELECT nom, fin_support, cout_annuel FROM logiciel WHERE id_logiciel = 1;
| nom | fin_support | cout_annuel |
|---|---|---|
| Microsoft 365 | NULL | 0.00 |
fin_support vaut NULL partout : la colonne est facultative et sans valeur par défaut. cout_annuel vaut 0.00 : une colonne NOT NULL ajoutée à une table non vide a besoin d'une valeur pour les lignes existantes, c'est le rôle du DEFAULT.
Tu avais trouvé ?
Exercice 4 sur 5 Choisir les actions référentielles Réussi À revoir
Le service informatique prête du matériel. On ajoute la table :
pret (id_pret, #id_poste, #id_employe, date_debut, date_retour_prevue, date_retour)
Pour chaque clé étrangère, choisis l'action ON DELETE et justifie-la, puis écris le CREATE TABLE complet. Un prêt en cours a une date_retour vide.
Voir la réponseMasquer la réponse de l'exercice 4
id_poste:RESTRICT. Un poste prêté ne doit pas disparaître de l'inventaire ; on garde aussi l'historique des prêts.id_employe:RESTRICT, pour la même raison : l'historique dit qui avait le matériel. Un employé parti n'est pas supprimé (voir l'avertissement surCASCADE).
CREATE TABLE pret (
id_pret INT AUTO_INCREMENT PRIMARY KEY,
id_poste INT NOT NULL,
id_employe INT NOT NULL,
date_debut DATE NOT NULL,
date_retour_prevue DATE NOT NULL,
date_retour DATE,
CONSTRAINT ck_pret_dates CHECK (date_retour_prevue >= date_debut
AND (date_retour IS NULL OR date_retour >= date_debut)),
CONSTRAINT fk_pret_poste FOREIGN KEY (id_poste)
REFERENCES poste (id_poste) ON DELETE RESTRICT,
CONSTRAINT fk_pret_employe FOREIGN KEY (id_employe)
REFERENCES employe (id_employe) ON DELETE RESTRICT
);
ON DELETE RESTRICT est le comportement par défaut : l'écrire rend ton intention visible.
Tu avais trouvé ?
Exercice 5 sur 5 Normaliser Réussi À revoir
Un stagiaire propose cette table pour les interventions :
intervention_v2 (id_intervention, id_ticket, titre_ticket, technicien_nom, technicien_tel, date_intervention, duree_min, pieces_changees)
Exemple de valeur pour pieces_changees : 'ventilateur, pâte thermique'. Repère les problèmes de normalisation et propose une correction.
Voir la réponseMasquer la réponse de l'exercice 5
pieces_changeescontient une liste : la table viole la 1FN. Crée une tablepiece_changee (#id_intervention, piece), une ligne par pièce.titre_ticketdépend deid_ticket, une colonne non clé : la table viole la 3FN. Le titre est déjà dansticket.technicien_nomettechnicien_teldécrivent le technicien, pas l'intervention : même problème. Remplace-les par une clé étrangère#id_technicienversemploye.
On retrouve la table intervention de la base d'exemple.
Tu avais trouvé ?