Systèmes d'information
Définition d'un SI, ses 4 composantes (Individus, Matériel, Logiciels/Procédures, Données), les fonctions d'un SI, la différence entre SI et système informatique, et exemples concrets d'entreprises marocaines.
- Définir un Système d'Information et le distinguer d'un système informatique
- Identifier les 4 composantes d'un SI : Individus, Matériel, Logiciels/Procédures, Données
- Comprendre les 4 fonctions d'un SI : collecter, stocker, traiter, diffuser
- Reconnaître les niveaux d'information dans une organisation (opérationnel, tactique, stratégique)
Un Système d'Information (SI) est un ensemble organisé de ressources (humaines, matérielles, logicielles et données) permettant de collecter, stocker, traiter et diffuser de l'information au sein d'une organisation pour aider à la prise de décision.
SI vs Système Informatique — ne pas confondre
Le système informatique est uniquement la partie technique (ordinateurs, serveurs, réseaux). Il est une sous-composante du SI. Un SI existait avant les ordinateurs — les registres papier d'une administration marocaine des années 1960 constituaient déjà un SI.
Exemple : la Direction Régionale des Impôts de Casablanca possède un SI composé de : agents (individus), ordinateurs et serveurs (matériel), logiciel de gestion fiscale + procédures internes (logiciels), dossiers des contribuables (données). L'ensemble forme le SI, pas uniquement les ordinateurs.
| Critère | Système d'Information | Système Informatique |
|---|---|---|
| Périmètre | Humain + technique + organisationnel | Uniquement technique |
| Composantes | Individus, matériel, logiciels, données | Serveurs, réseaux, logiciels système |
| Existence sans ordinateur | Oui — systèmes papier | Non par définition |
| Objectif | Aider à la décision | Automatiser des traitements |
| Composante | Définition | Exemple SkolaBase (Centre de Formation) |
|---|---|---|
| 1. Individus | Personnes qui utilisent, font fonctionner ou décident grâce au SI : utilisateurs, informaticiens, managers | Formateurs, étudiants, secrétaires, directeur pédagogique |
| 2. Matériel | Tous les équipements physiques : ordinateurs, serveurs, imprimantes, réseaux, scanners | PC de la salle informatique, serveur MySQL, imprimante convocations, réseau WiFi |
| 3. Logiciels & Procédures | Programmes informatiques ET procédures humaines (règles, modes opératoires, formulaires) | Application de gestion des inscriptions, procédure d'inscription, règlement intérieur |
| 4. Données | Toutes les informations traitées et stockées : fichiers, BDD, archives papier ou numériques | Dossiers étudiants, notes, emplois du temps, contrats de formation |
Retenez IMLD : Individus — Matériel — Logiciels/procédures — Données. Ou l'image d'un restaurant : les serveurs (I), la cuisine et les ustensiles (M), les recettes et les règles d'hygiène (L), les commandes des clients (D).
COLLECTER STOCKER TRAITER DIFFUSER
| | | |
Fiches d'inscription Base de données Calcul des notes Bulletins de notes
Feuilles de présence Fichiers Excel Statistiques Convocations examen
Résultats examens Archives papier Emplois du temps Attestations
↓ ↓ ↓ ↓
Saisie/scan MySQL / Cloud Logiciel gestion Email / Impression
Collecter : acquérir les informations depuis l'environnement (inscriptions, résultats, présences). Stocker : conserver les informations de façon organisée et durable (BDD, archives). Traiter : transformer les données brutes en informations utiles (calcul de moyenne, statistiques de réussite). Diffuser : distribuer les résultats aux bonnes personnes au bon moment (bulletins aux étudiants, rapports à la direction).
| Niveau | Utilisateurs | Type d'information | Exemple SkolaBase | Horizon temporel |
|---|---|---|---|---|
| Opérationnel | Employés, techniciens | Détaillée, précise, temps réel | Liste des présences du jour, notes d'un examen | Journalier |
| Tactique | Chefs de service, responsables | Agrégée, synthétique, périodique | Taux de réussite par filiale, bilan mensuel inscriptions | Mensuel/Trimestriel |
| Stratégique | Direction générale | Résumée, prospective, comparative | Rentabilité des formations, ouverture nouvelles filières | Annuel/Pluriannuel |
Projet informatique & Cahier des Charges
Les 5 phases d'un projet informatique (schéma directeur, étude préalable, étude détaillée, réalisation, mise en œuvre), définition et structure du CDC, différence CDCF et CDCT.
- Les 5 phases d'un projet informatique et leur ordre logique
- Définir le Cahier des Charges (CDC) et ses 5 sections
- Distinguer CDCF (Fonctionnel) et CDCT (Technique)
- Comprendre pourquoi le CDC est le document fondateur de tout projet
Phase 1 Phase 2 Phase 3 Phase 4 Phase 5
SCHEMA ETUDE ETUDE REALISATION MISE EN
DIRECTEUR PREALABLE DETAILLEE OEUVRE
| | | | |
Orientation Faisabilité Spécifications Développement Déploiement
stratégique Analyse besoins Techniques Tests Formation
Budget global Etude exist. MCD / MLD BDD + appli Maintenance
Plan 3-5 ans Proposition Maquettes Recette Evolution
| Phase | Objectif principal | Livrable | SkolaBase — exemple |
|---|---|---|---|
| 1. Schéma directeur | Définir la vision stratégique SI à 3-5 ans | Plan directeur SI | Décision de numériser la gestion du centre |
| 2. Étude préalable | Analyser la faisabilité, l'existant, les besoins | Rapport de faisabilité | Inventaire des registres papier, entretiens avec le personnel |
| 3. Étude détaillée | Spécifier précisément ce qui sera construit | CDC + MCD + MLD | Dictionnaire de données, MCD, maquettes écrans |
| 4. Réalisation | Développer et tester l'application | Application testée | Création BDD MySQL, développement, tests |
| 5. Mise en œuvre | Déployer, former, maintenir | Système en production | Installation serveur, formation secrétaires, support |
Beaucoup de projets échouent parce qu'ils sautent la Phase 3 pour coder directement. Résultat : on développe une application qui ne correspond pas aux besoins réels. La règle d'or : 1 heure d'analyse évite 10 heures de développement erroné.
Le Cahier des Charges est un document contractuel qui décrit précisément les besoins du client (maîtrise d'ouvrage) et ce que le prestataire (maîtrise d'œuvre) doit réaliser. C'est le contrat technique et fonctionnel du projet — toute ambigüité dans le CDC engendrera des litiges et des surcoûts.
| # | Section du CDC | Contenu | SkolaBase — exemple |
|---|---|---|---|
| 1 | Présentation du projet | Contexte, objectifs, périmètre, acteurs | Centre de formation YY, 500 étudiants/an, 3 filières |
| 2 | Description de l'existant | Situation actuelle, problèmes à résoudre | Inscriptions sur registres papier, pertes de données, doublons |
| 3 | Expression des besoins | Fonctionnalités attendues, contraintes | Gérer inscriptions, planifier sessions, générer bulletins |
| 4 | Contraintes techniques | Technologies imposées, environnement, sécurité | MySQL 8, Windows Server, 3 postes simultanés |
| 5 | Livrables et planning | Ce qui doit être livré, échéances, budget | BDD + application + documentation, livraison en 3 mois |
| Critère | CDCF — Cahier des Charges Fonctionnel | CDCT — Cahier des Charges Technique |
|---|---|---|
| Rédigé par | Le client (maîtrise d'ouvrage) | Le prestataire (maîtrise d'œuvre) |
| Langage | Métier, fonctionnel — sans jargon technique | Technique — langages, architectures, BDD |
| Répond à | Quoi faire ? Pour qui ? Pourquoi ? | Comment le faire ? Avec quoi ? |
| Exemple SkolaBase | « Le système doit permettre d'inscrire un étudiant » | « Table Etudiant : numCIN VARCHAR(8) PK, nomEtu VARCHAR(50) NN » |
Périmètre & Contraintes du projet
Délimiter précisément le périmètre d'un projet SI — 4 étapes (buts, livrables, tâches, contraintes), le triangle Budget/Délai/Portée, et les 5 questions clés pour fixer les limites.
- Définir le périmètre d'un projet et pourquoi il est critique
- Appliquer les 4 étapes de délimitation : buts, livrables, tâches, contraintes
- Comprendre le triangle Budget/Délai/Portée (trilemme du projet)
- Poser les 5 questions clés pour fixer les limites du périmètre
Le périmètre du projet (ou scope) définit précisément ce qui est inclus et ce qui est exclu du projet. Un périmètre mal défini est la première cause d'échec des projets informatiques : le client demande plus que prévu, le prestataire dépasse son budget, et les délais explosent.
Exemple SkolaBase : le client dit « je veux gérer mon centre de formation ». Cela peut vouloir dire 50 choses différentes. Le périmètre doit préciser :
- Inclus : gestion des inscriptions, planning des sessions, gestion des notes, génération des bulletins
- Exclus : gestion de la paie des formateurs, comptabilité générale, site web public, application mobile
Ce qui n'est pas explicitement inclus est exclu. Cette règle protège les deux parties.
| Étape | Question clé | SkolaBase — exemple |
|---|---|---|
| 1. Définir les buts | Pourquoi fait-on ce projet ? Quel problème résout-on ? | Eliminer les registres papier, réduire les erreurs de saisie, accélérer la génération des bulletins de 3 jours à 5 minutes |
| 2. Définir les livrables | Qu'est-ce qui sera remis à la fin ? Quels documents, logiciels ? | Base de données MySQL, application de gestion, documentation technique, manuel utilisateur, formation du personnel |
| 3. Définir les tâches | Quelles activités faut-il réaliser pour produire les livrables ? | Analyse Merise, création BDD, développement interface, tests, déploiement, formation |
| 4. Définir les contraintes | Quelles sont les limites (budget, temps, technique) ? | Budget : 50 000 MAD max, Délai : 3 mois, Technique : MySQL + Windows |
PORTÉE
(fonctionnalités)
/\
/ \
/ \
/ ★ \
/ QUALITÉ \
/____________\
BUDGET DÉLAI
(coût) (temps)
Principe fondamental :
Si PORTEE augmente → BUDGET ou DELAI doit augmenter
Si DELAI est réduit → BUDGET augmente ou PORTEE diminue
Si BUDGET est réduit → DELAI augmente ou PORTEE diminue
On ne peut pas optimiser les 3 simultanément
Exemple concret SkolaBase : le client veut toutes les fonctionnalités (portée élevée) en 1 mois (délai court) pour 10 000 MAD (budget faible). C'est impossible. Le chef de projet doit aider le client à arbitrer : soit réduire les fonctionnalités (Phase 1 sans bulletins PDF), soit augmenter le budget, soit allonger le délai.
| # | Question à poser pour fixer les limites | SkolaBase — réponse attendue |
|---|---|---|
| 1 | Quelles sont les fonctionnalités absolument indispensables (Must Have) ? | Inscriptions, sessions, notes, bulletins |
| 2 | Quelles fonctionnalités peut-on reporter à une version 2 (Nice to Have) ? | Application mobile, statistiques avancées |
| 3 | Quels systèmes existants faut-il intégrer ou migrer ? | Migrer les données des registres papier 2022-2024 |
| 4 | Qui sont les utilisateurs et quels sont leurs droits ? | Admin (tout), secrétaire (inscriptions), formateur (notes) |
| 5 | Quelles sont les contraintes légales ou réglementaires ? | Protection des données personnelles, conservation 5 ans |
Introduction aux Bases de données
Définition d'une BDD, ses 4 objectifs, les 4 phases de conception (analyse, modélisation conceptuelle, modélisation logique, implémentation), et identification des entités et données candidates depuis le CDC.
- Définir une base de données et la distinguer d'un fichier Excel ou d'un fichier texte
- Identifier les 4 objectifs fondamentaux d'une BDD (intégrité, partage, sécurité, non-redondance)
- Connaître les 4 phases de conception : Analyse → MCD → MLD → Implémentation
- Lire un CDC et identifier les entités candidates et leurs données
Une base de données est un ensemble structuré et persistant de données liées entre elles, géré par un Système de Gestion de Base de Données (SGBD), accessible par plusieurs utilisateurs simultanément, et conçu pour minimiser la redondance.
| Critère | Fichier Excel / CSV | Base de données MySQL |
|---|---|---|
| Accès simultané | Problèmes de conflits si plusieurs utilisateurs | Natif — gestion des verrous, transactions |
| Intégrité des données | Aucune contrainte — on peut mettre n'importe quoi | Clés primaires, étrangères, NOT NULL, CHECK |
| Volume de données | Limité (1 048 576 lignes Excel max) | Milliards d'enregistrements |
| Sécurité | Mot de passe fichier uniquement | Utilisateurs, rôles, privilèges granulaires |
| Relations entre données | Manuelles — erreurs fréquentes | Clés étrangères — intégrité référentielle automatique |
| Redondance | Élevée — même info dans plusieurs cellules | Réduite grâce à la normalisation |
| # | Objectif d'une BDD | Explication | Exemple SkolaBase |
|---|---|---|---|
| 1 | Non-redondance | Chaque information est stockée une seule fois | Le nom d'un étudiant n'est saisi qu'une fois, pas dans chaque note |
| 2 | Intégrité | Les données respectent des règles de validité | Impossible d'enregistrer une note > 20 ou un étudiant sans CIN |
| 3 | Partage | Plusieurs utilisateurs accèdent simultanément | La secrétaire inscrit pendant que le formateur saisit les notes |
| 4 | Sécurité | Contrôle d'accès selon les rôles | Le formateur voit seulement ses étudiants, pas les données financières |
PHASE 1 PHASE 2 PHASE 3 PHASE 4
ANALYSE MCD MLD IMPLEMENTATION
| | | |
Lire le CDC Entités Tables SQL CREATE TABLE
Identifier Associations Clés primaires INSERT INTO
entités Cardinalités Clés étrangères Requêtes
Dictionnaire Formes norm. Contraintes Index
de données Graphe DF Types de données
| | | |
Niveau Niveau Niveau Niveau
conceptuel conceptuel logique physique
(métier) (indép. SGBD) (dépend SGBD) (MySQL 8)
| Phase | Niveau | Outil/Méthode | Résultat |
|---|---|---|---|
| 1. Analyse | Conceptuel métier | Lecture CDC, entretiens, RG | Dictionnaire de données, Graphe des DF |
| 2. MCD | Conceptuel (indépendant du SGBD) | Merise, entités-associations | Modèle Conceptuel de Données |
| 3. MLD | Logique (dépend du type de SGBD) | Règles de passage MCD→MLD | Modèle Logique de Données (tables) |
| 4. Implémentation | Physique (MySQL spécifique) | SQL DDL : CREATE TABLE | Base de données opérationnelle |
Lors de la lecture du CDC, on souligne les noms (substantifs) qui représentent des entités candidates (choses dont on veut stocker des informations) et les verbes qui représentent des associations entre ces entités.
CDC SkolaBase — Centre de Formation (extrait)
« Le centre gère des ETUDIANTS qui s'inscrivent à des SESSIONS
de FORMATION. Chaque session est animée par un FORMATEUR.
Les étudiants reçoivent des notes à chaque session.
Chaque formation appartient à une FILIERE. »
Entités candidates identifiées :
■ ETUDIANT → numCIN, nomEtu, prenomEtu, dateNaiss, ville
■ FORMATION → codeForm, titreForm, dureeForm, niveauForm
■ SESSION → codeSess, dateDebut, dateFin, lieuSess
■ FORMATEUR → numFormateur, nomForm, specialite
■ FILIERE → codeFiliere, libelleFiliere
Associations identifiées :
■ ETUDIANT ---[s'inscrit]--- SESSION
■ SESSION ---[est animée par]--- FORMATEUR
■ SESSION ---[porte sur]--- FORMATION
■ FORMATION ---[appartient à]--- FILIERE
Règles de gestion
Identifier les règles de gestion depuis un CDC, les traduire en tableau numéroté (RG1…RGn) qui relie les entités entre elles — fondement de toute la modélisation Merise — application complète sur SkolaBase.
- Définir une règle de gestion et la distinguer d'une contrainte technique
- Lire un CDC et extraire les RG de manière systématique
- Traduire chaque RG en tableau numéroté : entité source → verbe → entité cible + quantité
- Compléter le tableau des RG de SkolaBase (6 règles)
Une règle de gestion (RG) est une contrainte métier qui décrit comment les entités du système se comportent et interagissent. Elle est exprimée en langage naturel (pas technique) et provient directement du CDC ou des entretiens avec les utilisateurs. Les RG sont le pont entre le CDC et le MCD.
Pourquoi les RG sont-elles critiques ?
Les RG déterminent directement les cardinalités du MCD. Une erreur dans les RG entraîne un MCD faux, un MLD faux, et donc une BDD qui ne correspond pas aux besoins réels. Reformuler les RG avec le client avant de modéliser est la pratique la plus importante de Merise.
| Critère | Règle de gestion | Contrainte technique |
|---|---|---|
| Langage | Métier, naturel | Technique (SQL, code) |
| Source | CDC, utilisateurs | Informaticien/DBA |
| Exemple | « Un étudiant peut s'inscrire à plusieurs sessions » | FOREIGN KEY (numEtu) REFERENCES Etudiant(numCIN) |
| Impact | Cardinalités du MCD | Implémentation BDD |
Texte du CDC :
« Le centre gère des ETUDIANTS. Chaque étudiant possède un numéro de CIN unique.
Les étudiants peuvent s'inscrire à plusieurs SESSIONS de FORMATION.
Un étudiant ne peut s'inscrire qu'une seule fois à une même session.
Chaque session est animée par un seul FORMATEUR.
Un formateur peut animer plusieurs sessions au cours de l'année.
Chaque session porte sur une seule FORMATION.
Une formation peut donner lieu à plusieurs sessions différentes.
Chaque formation appartient à une seule FILIERE.
Une filière regroupe plusieurs formations. »
► Souligner les entités : ETUDIANT, SESSION, FORMATION, FORMATEUR, FILIERE
► Souligner les verbes : s'inscrire, animer, porter sur, appartenir à
► Extraire les quantités : « plusieurs » (N), « un seul » (1), « une seule fois » (1)
Chaque RG se lit dans les deux sens : « Un A peut avoir [0,1 ou N] B » ET « Un B peut avoir [0,1 ou N] A ». Les deux sens donnent les deux cardinalités de l'association dans le MCD.
| N° | Entité source | Verbe (association) | Entité cible | Card. source | Card. cible | Lecture |
|---|---|---|---|---|---|---|
| RG1 | ETUDIANT | s'inscrit à | SESSION | 0,N | 1,N | Un étudiant peut s'inscrire à 0 ou plusieurs sessions. Une session a au moins 1 étudiant inscrit. |
| RG2 | FORMATEUR | anime | SESSION | 0,N | 1,1 | Un formateur anime 0 ou plusieurs sessions. Une session est animée par exactement 1 formateur. |
| RG3 | SESSION | porte sur | FORMATION | 1,N | 1,1 | Une session porte sur exactement 1 formation. Une formation peut avoir 1 ou plusieurs sessions. |
| RG4 | FORMATION | appartient à | FILIERE | 1,1 | 1,N | Une formation appartient à exactement 1 filière. Une filière regroupe 1 ou plusieurs formations. |
| RG5 | ETUDIANT | obtient | NOTE (sur SESSION) | — | — | L'inscription (RG1) porte une note — la note est un attribut de l'association INSCRIPTION |
| RG6 | FORMATEUR | spécialisé dans | FILIERE | 0,N | 0,N | Un formateur peut être spécialisé dans plusieurs filières. Une filière peut avoir plusieurs formateurs spécialisés. |
La note n'est pas une entité indépendante — elle n'existe que dans le contexte d'une inscription d'un étudiant à une session. Elle sera donc placée comme attribut de l'association INSCRIPTION (entre ETUDIANT et SESSION) dans le MCD. C'est ce qu'on appelle une association porteuse de donnée.
Dictionnaire de données
Construire le Dictionnaire de Données (DD) : structure (code/désignation/type/taille/observations), les 5 types, convention de nommage objetConcept, contrôles qualité (polysémies, synonymes, données calculées), DD complet SkolaBase.
- Distinguer Concept, Donnée et Valeur
- Construire la structure du DD : 5 colonnes obligatoires
- Maîtriser les 5 types de données (A, N, AN, D, B)
- Appliquer la convention de nommage
objetConcept(ex :numCINEtu) - Détecter et corriger polysémies, synonymes et données calculées
- Produire le DD complet de SkolaBase (16 données)
| Niveau | Définition | Exemple SkolaBase | Contre-exemple |
|---|---|---|---|
| Concept | Catégorie abstraite dont on veut stocker des informations — c'est l'entité | ETUDIANT, SESSION, FORMATEUR | « personne » est trop vague sans contexte |
| Donnée | Propriété d'un concept — une colonne dans la future table | nom de l'étudiant, date de début de session | « âge » est calculé (pas une donnée brute) |
| Valeur | Contenu concret d'une donnée pour une occurrence | « Alaoui », « 2024-09-01 » | Une valeur seule sans contexte n'a pas de sens |
| Colonne | Contenu | Exemple |
|---|---|---|
| Code | Nom technique de la donnée — convention objetConcept | numCINEtu, nomEtu, dateDebutSess |
| Désignation | Libellé clair en français pour l'utilisateur | Numéro CIN de l'étudiant, Nom de l'étudiant |
| Type | Nature de la donnée (voir tableau types) | AN, A, D, N, B |
| Taille | Longueur maximale ou précision | 8, 50, —, 4,2 |
| Observations | Contraintes, remarques, valeurs possibles | Unique, PK, entre 0 et 20, format JJ/MM/AAAA |
| Type | Nom complet | Description | Exemples | SQL correspondant |
|---|---|---|---|---|
| A | Alphabétique | Lettres uniquement (pas de chiffres) | nomEtu, prenomEtu, libelleFiliere | CHAR, VARCHAR (lettres) |
| N | Numérique | Chiffres, virgule, signe | noteEtu, dureeForm, nbrePlaces | INT, DECIMAL, FLOAT |
| AN | Alphanumérique | Lettres ET chiffres mélangés | numCINEtu, codeForm, codeSess | VARCHAR, CHAR |
| D | Date | Date, heure ou date+heure | dateNaissEtu, dateDebutSess, dateFin | DATE, DATETIME, TIMESTAMP |
| B | Booléen | Vrai/Faux, Oui/Non, 0/1 | actifFormateur, certifiantForm | BOOLEAN, TINYINT(1) |
objetConceptLa convention Merise : abréviation de la propriété + abréviation de l'entité, en camelCase. Exemples : numCINEtu = numéro CIN + Etudiant, titreForm = titre + Formation, dateDebutSess = date de début + Session. Cette convention évite les ambigüités quand plusieurs entités ont des propriétés au même nom (ex : nomEtu vs nomForm).
| Problème | Définition | Exemple à corriger | Solution |
|---|---|---|---|
| Polysémie | Un même mot désigne des choses différentes selon le contexte | « nom » peut être nom étudiant, nom formateur ou nom formation | Renommer : nomEtu, nomFormateur, titreForm |
| Synonymes | Deux mots différents désignent la même chose | « élève » et « apprenant » et « étudiant » dans le même CDC | Choisir un seul terme : nomEtu partout |
| Donnée calculée | Valeur déductible depuis d'autres données — à exclure du DD | « âge étudiant » = YEAR(NOW()) - YEAR(dateNaissEtu) | Conserver dateNaissEtu, calculer l'âge à la demande avec SQL |
| Imprécision | Donnée trop vague pour être modélisée | « coordonnées » — téléphone ? email ? adresse ? | Décomposer : telEtu, emailEtu, adresseEtu |
| Code | Désignation | Type | Taille | Observations | Entité |
|---|---|---|---|---|---|
numCINEtu | Numéro CIN de l'étudiant | AN | 8 | Identifiant unique, format : lettre+7 chiffres | ETUDIANT |
nomEtu | Nom de famille de l'étudiant | A | 50 | Obligatoire, majuscules | ETUDIANT |
prenomEtu | Prénom de l'étudiant | A | 50 | Obligatoire | ETUDIANT |
dateNaissEtu | Date de naissance de l'étudiant | D | — | Format JJ/MM/AAAA, âge calculé à la demande | ETUDIANT |
villeEtu | Ville de résidence de l'étudiant | A | 50 | Villes marocaines | ETUDIANT |
telEtu | Téléphone de l'étudiant | AN | 15 | Format marocain 06/07XXXXXXXX, nullable | ETUDIANT |
numFormateur | Identifiant du formateur | N | 4 | Clé primaire auto-incrémentée | FORMATEUR |
nomFormateur | Nom du formateur | A | 50 | Obligatoire | FORMATEUR |
specialiteForm | Spécialité principale du formateur | A | 80 | Nullable | FORMATEUR |
codeSess | Code de la session | AN | 10 | Format : SESS-AAAA-NN (ex: SESS-2024-01) | SESSION |
dateDebutSess | Date de début de la session | D | — | Obligatoire | SESSION |
dateFinSess | Date de fin de la session | D | — | Doit être > dateDebutSess | SESSION |
lieuSess | Lieu de déroulement de la session | AN | 100 | Salle + ville | SESSION |
codeForm | Code de la formation | AN | 10 | Clé primaire, unique | FORMATION |
titreForm | Intitulé de la formation | AN | 100 | Obligatoire | FORMATION |
dureeForm | Durée de la formation en heures | N | 3 | Entier positif, ex: 120 | FORMATION |
niveauForm | Niveau requis pour la formation | A | 20 | Valeurs : Bac, Bac+2, Bac+3, Technicien | FORMATION |
codeFiliere | Code de la filière | AN | 6 | Clé primaire, ex: INF, GES, ELE | FILIERE |
libelleFiliere | Libellé complet de la filière | A | 80 | Ex: Informatique, Gestion, Électronique | FILIERE |
noteInscription | Note obtenue à la session | N | 4,2 | Entre 0 et 20 — attribut de l'association INSCRIPTION | INSCRIPTION |
agEtu (âge de l'étudiant) dans le DD. Que lui répondre ?Dépendances fonctionnelles
Définition d'une DF (A→B), DF directes vs transitives vs composées (A+B→C), méthode de construction du graphe des DF, et graphe complet de SkolaBase prét pour dériver le MCD.
- Définir une DF : A→B signifie « connaissant A, on connaît un seul B »
- Distinguer DF directe, DF transitive (à éliminer) et DF composée
- Construire le graphe des DF à partir du DD et des RG
- Identifier les sources des DF — elles deviendront les identifiants des entités
On dit que la donnée A détermine fonctionnellement B (noté A→B) si, pour toute valeur de A, il existe au plus une valeur de B. A est le déterminant, B est le dépendant. En clair : connaissant A, on peut retrouver exactement un B — jamais plusieurs.
| DF | Lecture | Valide ? | Justification |
|---|---|---|---|
numCINEtu → nomEtu | Connaissant le CIN, on connaît un seul nom | Oui | Chaque CIN est unique — un étudiant = un seul nom |
numCINEtu → dateNaissEtu | Connaissant le CIN, on connaît une seule date de naissance | Oui | Un étudiant a une seule date de naissance |
nomEtu → numCINEtu | Connaissant le nom, on connaît un seul CIN | Non | Plusieurs étudiants peuvent avoir le même nom (Alaoui) |
codeSess → dateDebutSess | Connaissant le code session, on connaît une seule date de début | Oui | Une session a une seule date de début |
codeForm → codeFiliere | Connaissant le code formation, on connaît une seule filière | Oui | Une formation appartient à une seule filière (RG4) |
Si D1→D2 et D2→D3, alors D1→D3 est une DF transitive (elle est déductible, pas directe). On ne la conserve pas dans le graphe des DF — elle serait redondante et fausserait le MCD. Exemple : codeForm→codeFiliere et codeFiliere→libelleFiliere → donc codeForm→libelleFiliere est transitive et ne figure pas dans le graphe.
| Type | Définition | Exemple SkolaBase | Garder dans graphe ? |
|---|---|---|---|
| DF directe | A détermine B sans intermédiaire | numCINEtu → nomEtu | Oui |
| DF transitive | A→B→C : A détermine C via B | codeForm → libelleFiliere (via codeFiliere) | Non — redondante |
| DF composée | A+B détermine C (deux déterminants) | numCINEtu + codeSess → noteInscription | Oui — clé composée |
numCINEtu + codeSess → noteInscriptionConnaissant à la fois le CIN de l'étudiant ET le code de la session, on peut retrouver une seule note. En effet, un même étudiant peut avoir plusieurs notes (une par session) et une même session a plusieurs notes (une par étudiant inscrit). Seule la combinaison des deux détermine une note unique.
┌─────── nomEtu
├─────── prenomEtu
numCINEtu ─────────┼─────── dateNaissEtu
├─────── villeEtu
└─────── telEtu
┌─────── nomFormateur
numFormateur ──────┼─────── specialiteForm
└─────── numFormateur ──────→ codeSess (via RG2)
┌─────── dateDebutSess
├─────── dateFinSess
codeSess ───────────┼─────── lieuSess
├─────── codeForm (via RG3)
└─────── numFormateur (via RG2)
┌─────── titreForm
├─────── dureeForm
codeForm ───────────┼─────── niveauForm
└─────── codeFiliere (via RG4)
codeFiliere ────────────→ libelleFiliere
DF composée :
numCINEtu + codeSess ────────→ noteInscription
Sources identifiées (futurs identifiants des entités) :
numCINEtu → entité ETUDIANT
numFormateur → entité FORMATEUR
codeSess → entité SESSION
codeForm → entité FORMATION
codeFiliere → entité FILIERE
codeForm → libelleFiliere : est-ce une DF directe à inclure dans le graphe ?MCD — Entités & méthode Merise
Présentation de Merise et ses 3 niveaux (MCD/MLD/MPD), définition d'une entité (identifiant, attributs, occurrences, formalisation), et les 5 règles de passage Graphe DF→MCD avec application des règles R1 et R2 sur SkolaBase.
- Merise : historique, 3 niveaux (MCD, MLD, MPD) et leur rôle respectif
- Entité : définition, identifiant, attributs, occurrences, formalisme graphique
- Les 5 règles de passage Graphe DF → MCD
- Application de R1 (donnée = propriété) et R2 (source DF = identifiant) sur SkolaBase
Merise est une méthode française de conception des systèmes d'information, développée dans les années 1970 par le ministère de l'Industrie et Hubert Tardieu. Très utilisée en France et dans les pays francophones (dont le Maroc). Son principe central : séparer la démarche (quoi concevoir) de la technologie (comment implémenter).
| Niveau | Modèle | Question | Dépendance SGBD | SkolaBase |
|---|---|---|---|---|
| Conceptuel | MCD — Modèle Conceptuel de Données | Quoi représenter ? | Non — universel | Entités + associations + cardinalités |
| Logique | MLD — Modèle Logique de Données | Comment organiser ? | Type SGBDR uniquement | Tables + clés primaires + clés étrangères |
| Physique | MPD — Modèle Physique de Données | Comment stocker ? | Oui — MySQL spécifique | SQL CREATE TABLE, types MySQL, index |
Une entité est un objet réel ou abstrait du domaine étudié, sur lequel on souhaite stocker des informations, et qui peut être identifié de manière unique. Elle correspond à une future table SQL.
┌───────────────────┐
│ ETUDIANT │ ← Nom de l'entité (en majuscules)
├───────────────────┤
│ #numCINEtu │ ← Identifiant (souligné, précédé de #)
├───────────────────┤
│ nomEtu │
│ prenomEtu │ ← Attributs (propriétés)
│ dateNaissEtu │
│ villeEtu │
└───────────────────┘
Occurrence (instance) : une ligne dans la future table
Ex : { numCINEtu:'A123456', nomEtu:'Alaoui', prenomEtu:'Karim', ... }
| Élément | Définition | Propriétés |
|---|---|---|
| Identifiant | Attribut(s) qui distingue une occurrence de toutes les autres | Unique, obligatoire, stable, non NULL — deviendra la clé primaire (PK) |
| Attributs | Propriétés qui décrivent l'entité | Atomiques (une seule valeur), non calculés, du DD |
| Occurrence | Valeurs concrètes pour une instance de l'entité | Une occurrence = une ligne dans la table SQL |
| Règle | Énoncé | Application SkolaBase |
|---|---|---|
| R1 | Toute donnée du graphe (non source) devient une propriété d'entité | nomEtu, prenomEtu, dateNaissEtu → attributs de ETUDIANT |
| R2 | Toute source d'une DF devient l'identifiant d'une entité | numCINEtu → identifiant de ETUDIANT ; codeSess → identifiant de SESSION |
| R3 | Une DF entre deux sources (A→B avec A et B sources) devient une association non porteuse (CIF — Contrainte d'Intégrité Fonctionnelle) | codeSess→codeForm (SESSION porte sur FORMATION) → association non porteuse |
| R4 | Une source issue d'une DF composée (A+B→C) devient une association porteuse (CIM — Contrainte d'Intégrité Multiple) | numCINEtu+codeSess→noteInscription → association INSCRIPTION porteuse de note |
| R5 | Une association entre entités qui n'apparaît pas dans le graphe DF (trouvée dans les RG) devient une association hors graphe (CIM ou CIF) | RG2 : FORMATEUR anime SESSION → association hors graphe (non déductible du graphe DF seul) |
Depuis le graphe DF, on identifie les sources (R2) :
numCINEtu → Entité ETUDIANT
Attributs (R1) : nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu
numFormateur → Entité FORMATEUR
Attributs (R1) : nomFormateur, specialiteForm
codeSess → Entité SESSION
Attributs (R1) : dateDebutSess, dateFinSess, lieuSess
codeForm → Entité FORMATION
Attributs (R1) : titreForm, dureeForm, niveauForm
codeFiliere → Entité FILIERE
Attributs (R1) : libelleFiliere
Donnée composée (R4) :
numCINEtu + codeSess → noteInscription
→ Association INSCRIPTION porteuse de noteInscription
codeSess → codeForm — quelle règle Merise s'applique et quel élément du MCD cela génère ?MCD — Associations & Cardinalités
Définir et représenter les associations (binaires, ternaires, porteuses), maîtriser les 4 types de cardinalités (0,1 / 1,1 / 0,N / 1,N), les déduire des RG, appliquer R3/R4/R5, et construire le MCD complet de SkolaBase.
- Définir une association et ses types (binaire, ternaire, porteuse)
- Lire et déduire les cardinalités depuis les RG
- Comprendre les 4 types : (0,1) / (1,1) / (0,N) / (1,N)
- Appliquer R3 (association non porteuse), R4 (porteuse), R5 (hors graphe)
- Dessiner et lire le MCD complet de SkolaBase
Une association (ou relation en Merise) représente un lien sémantique entre deux ou plusieurs entités. Elle traduit une RG du CDC. Dans le formalisme graphique, elle est représentée par un losange avec le nom du verbe qui la décrit.
Association binaire (entre 2 entités) :
ETUDIANT ─(0,N)─◆ s'inscrit ◆─(1,N)─ SESSION
Association porteuse (avec attribut) :
ETUDIANT ─(0,N)─◆ INSCRIPTION ◆─(1,N)─ SESSION
noteInscription ← attribut de l'association
Association ternaire (entre 3 entités) :
SALLE
|
FORMATEUR ─◆ TIENT ◆─ SESSION
(rare — éviter si possible)
| Type | Définition | Exemple SkolaBase |
|---|---|---|
| Binaire | Relie exactement 2 entités | FORMATION appartient à FILIERE |
| Ternaire | Relie 3 entités simultanément | Rare dans SkolaBase — à éviter |
| Porteuse | Association avec au moins un attribut propre (CIM) | INSCRIPTION entre ETUDIANT et SESSION, porte noteInscription |
| Non porteuse | Association sans attribut propre (CIF) | SESSION porte sur FORMATION (pas d'attribut sur le lien) |
La cardinalité (min, max) se lit : « une occurrence de cette entité est liée à minimum min et maximum max occurrences de l'autre entité ». Pour la déduire : poser deux questions à partir de la RG.
| Cardinalité | Min | Max | Signification | Exemple SkolaBase |
|---|---|---|---|---|
| (0,1) | Facultatif | 1 seul | Au plus un lien, pas obligé | Un formateur peut être affecté à 0 ou 1 service |
| (1,1) | Obligatoire | 1 seul | Exactement un lien, ni plus ni moins | Une session est animée par exactement 1 formateur |
| (0,N) | Facultatif | Plusieurs | Zéro ou plusieurs liens possibles | Un étudiant peut s'inscrire à 0 ou plusieurs sessions |
| (1,N) | Obligatoire | Plusieurs | Au moins un lien, plusieurs possibles | Une formation a au moins 1 session |
RG1 : « Un étudiant peut s'inscrire à plusieurs sessions.
Un étudiant ne s'inscrit pas forcément à une session (nouveau inscrit). »
Question 1 (du côté ETUDIANT) :
« Un ETUDIANT est lié à COMBIEN de SESSION ? »
Réponse : minimum 0 (pas obligé), maximum N (plusieurs) → cardinalité ETUDIANT = (0,N)
Question 2 (du côté SESSION) :
« Une SESSION est liée à COMBIEN d'ETUDIANT ? »
Réponse : minimum 1 (une session a au moins 1 étudiant), maximum N → cardinalité SESSION = (1,N)
Représentation :
ETUDIANT (0,N) ◆ INSCRIPTION ◆ (1,N) SESSION
┌──────────────┐ ┌───────────────┐
│ ETUDIANT │ │ FORMATEUR │
├──────────────┤ ├───────────────┤
│ #numCINEtu │ │ #numFormateur │
│ nomEtu │ │ nomFormateur │
│ prenomEtu │ │ specialite │
│ dateNaissEtu │ └───────────────┘
│ villeEtu │ |
└──────────────┘ (0,N)
| |
(0,N) ◆──── ANIME ────◆
| |
◆─ INSCRIPTION ─◆ (1,1)
noteInscription |
| ┌──────────────┐
(1,N) │ SESSION │
| ├──────────────┤
└─────────────────────────┤ #codeSess │
│ dateDebut │
│ dateFin │
│ lieu │
└──────────────┘
|
(1,N)
|
◆── PORTE SUR ──◆
|
(1,1)
|
┌──────────────┐
│ FORMATION │
├──────────────┤
│ #codeForm │
│ titreForm │
│ dureeForm │
│ niveauForm │
└──────────────┘
|
(1,N)
|
◆── APPARTIENT A ──◆
|
(1,1)
|
┌──────────────┐
│ FILIERE │
├──────────────┤
│ #codeFiliere │
│ libelleFiliere│
└──────────────┘
Formes normales
Rôle de la normalisation, 1FN (atomicité), 2FN (dépendance à la totalité de la clé), 3FN (dépendance directe à la clé), FNBC et 4FN — avec contre-exemples et corrections sur le cas SkolaBase.
- Pourquoi normaliser : éviter redondances, anomalies de mise à jour et incohérences
- 1FN : tout attribut doit être atomique et mono-valué
- 2FN : chaque attribut non-clé dépend de la totalité de la clé primaire
- 3FN : chaque attribut non-clé dépend directement de la clé (pas transitivement)
- FNBC : seules les DF dont le déterminant est une clé candidate
Une table non normalisée entraîne trois types d'anomalies : anomalie d'insertion (impossible d'insérer une formation sans session), anomalie de mise à jour (changer le titre d'une formation oblige à modifier N lignes), anomalie de suppression (supprimer la dernière session supprime aussi la formation). La normalisation élimine ces problèmes.
Table INSCRIPTION_BRUTE (non normalisée) :
numCINEtu | nomEtu | codeSess | titreForm | libelleFiliere | note
----------|---------|----------|------------------|----------------|-----
A123456 | Alaoui | SESS-01 | Dév. Web | Informatique | 14
A123456 | Alaoui | SESS-02 | Comptabilité | Gestion | 16
B789012 | Bennani | SESS-01 | Dév. Web | Informatique | 12
Problèmes :
✕ nomEtu répété pour chaque session → redondance
✕ Si "Dév. Web" change en "Développement Web" → modifier 2 lignes
✕ Si Alaoui se désinscrit de SESS-01 → on perd que SESS-01 existe
✕ Si SESS-01 a 0 inscrits → impossible de stocker la session
Une table est en 1FN si et seulement si tous ses attributs sont atomiques (une seule valeur par cellule) et mono-valués (pas de liste de valeurs dans une cellule).
| Situation | Exemple non-1FN | Correction en 1FN |
|---|---|---|
| Plusieurs prénoms dans une cellule | prenomEtu = "Karim, Mohamed" | Un seul prénom par ligne, ou table séparée PRENOM |
| Groupe répétitif dans une ligne | note1, note2, note3 dans la même table | Chaque note sur une ligne distincte avec codeSess |
| Donnée composite | adresse = "14 Rue Hassan II, Casablanca" | Deux attributs : rueEtu + villeEtu |
Notre DD est en 1FN : chaque attribut est atomique (nomEtu = un seul nom, dateDebutSess = une seule date...). Si le CDC avait dit « liste des prénoms », il aurait fallu créer une table PRENOM séparée.
Une table en 1FN est en 2FN si tout attribut non-clé dépend de la totalité de la clé primaire. La 2FN ne concerne que les tables à clé composée — une table avec une clé simple et en 1FN est automatiquement en 2FN.
Table INSCRIPTION (CLÉ : numCINEtu + codeSess) — non 2FN :
numCINEtu | codeSess | noteInscription | nomEtu | dateDebutSess
A123456 | SESS-01 | 14 | Alaoui | 2024-09-01
Problème :
numCINEtu → nomEtu (dépend de PARTIE de la clé → violation 2FN)
codeSess → dateDebutSess (dépend de PARTIE de la clé → violation 2FN)
numCINEtu + codeSess → noteInscription (dépend de TOUTE la clé → OK)
Correction — décomposer en 3 tables :
ETUDIANT(numCINEtu, nomEtu, ...)
SESSION(codeSess, dateDebutSess, ...)
INSCRIPTION(numCINEtu, codeSess, noteInscription)
Une table en 2FN est en 3FN si aucun attribut non-clé ne dépend transitivement de la clé primaire. En clair : un attribut non-clé ne doit pas déterminer un autre attribut non-clé.
Table FORMATION (non 3FN) :
codeForm | titreForm | dureeForm | codeFiliere | libelleFiliere
DEV01 | Dév. Web | 120 | INF | Informatique
CPT01 | Comptabilité| 80 | GES | Gestion
Problème :
codeForm → codeFiliere (OK — codeFiliere dépend de la clé)
codeFiliere → libelleFiliere (VIOLATION — un non-clé détermine un autre non-clé)
Donc : codeForm → codeFiliere → libelleFiliere (transitivité)
Correction — décomposer en 2 tables :
FORMATION(codeForm, titreForm, dureeForm, codeFiliere)
FILIERE(codeFiliere, libelleFiliere)
| Forme normale | Condition | Problème évité |
|---|---|---|
| 1FN | Attributs atomiques et mono-valués | Valeurs multiples dans une cellule |
| 2FN | 1FN + dépendance totale à la clé | Dépendance partielle (clé composée) |
| 3FN | 2FN + pas de transitivité | Non-clé détermine non-clé |
| FNBC | 3FN + tout déterminant est une clé candidate | DF élémentaires seules |
| 4FN | FNBC + pas de dépendances multivaluées indépendantes | Attributs multivalués indépendants |
SESSION(codeSess, dateDebut, dateFin, codeForm, titreForm) viole quelle forme normale ?MLD — Passage MCD→MLD
Les 4 règles de passage MCD→MLD : R1 (entité → table), R2 (cardinalité 1,1 : clé migrée), R3 (cardinalité N,N : table pivot), R4 (ternaire), entité forte/faible, cas particuliers, MLD complet SkolaBase.
- R1 : toute entité devient une table, l'identifiant devient la clé primaire (PK)
- R2 : association avec cardinalité maximale 1 d'un côté → clé étrangère (FK) migre vers la table « côté 1 »
- R3 : association avec cardinalités N,N des deux côtés → nouvelle table pivot à clé composée
- R4 : association ternaire → table avec les clés étrangères comme clé primaire
- Appliquer les 4 règles sur le MCD SkolaBase → MLD complet
Toute entité du MCD devient une table dans le MLD. Son identifiant devient la clé primaire (PK). Ses attributs deviennent les colonnes. Notation MLD : NOM_TABLE(cle_primaire, col1, col2, ...)
ETUDIANT (#numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu)
FORMATEUR (#numFormateur, nomFormateur, specialiteForm)
SESSION (#codeSess, dateDebutSess, dateFinSess, lieuSess)
FORMATION (#codeForm, titreForm, dureeForm, niveauForm)
FILIERE (#codeFiliere, libelleFiliere)
Quand une association possède une cardinalité maximale 1 d'un côté — notation (x,1) — la table du côté «1» reçoit la clé primaire de l'autre table comme clé étrangère (FK). Pas de nouvelle table créée.
Association ANIME : FORMATEUR (0,N) ◆────◆ (1,1) SESSION
→ Côté SESSION a cardinalité max = 1
→ SESSION reçoit la FK de FORMATEUR : numFormateur
SESSION (#codeSess, dateDebutSess, dateFinSess, lieuSess, #numFormateur FK)
Association PORTE SUR : SESSION (1,N) ◆────◆ (1,1) FORMATION
→ Côté FORMATION a cardinalité max = 1 ? Non !
Attention : la SESSION a cardinalité (1,1) du côté FORMATION
→ C'est SESSION qui a max=1 vis-à-vis de FORMATION
→ SESSION reçoit la FK de FORMATION : codeForm
SESSION (#codeSess, dateDebutSess, dateFinSess, lieuSess, #numFormateur FK, #codeForm FK)
Association APPARTIENT A : FORMATION (1,N) ◆────◆ (1,1) FILIERE
→ FORMATION a cardinalité max = 1 vis-à-vis de FILIERE
→ FORMATION reçoit la FK de FILIERE : codeFiliere
FORMATION (#codeForm, titreForm, dureeForm, niveauForm, #codeFiliere FK)
Quand les deux côtés d'une association ont une cardinalité maximale N — (x,N) des deux côtés — l'association devient une nouvelle table (table pivot) dont la clé primaire est composée des clés primaires des deux entités. Les attributs portés de l'association deviennent des colonnes de cette table pivot.
Association INSCRIPTION : ETUDIANT (0,N) ◆ INSCRIPTION ◆ (1,N) SESSION
→ Les deux côtés ont cardinalité max = N
→ Créer une table pivot INSCRIPTION
→ Clé composée : (numCINEtu + codeSess)
→ Attribut porté noteInscription devient colonne
INSCRIPTION (#numCINEtu FK, #codeSess FK, noteInscription)
Notation : #champ = clé primaire (PK) | *champ = clé étrangère (FK)
FILIERE (#codeFiliere, libelleFiliere)
FORMATION (#codeForm, titreForm, dureeForm, niveauForm,
*codeFiliere → FILIERE.codeFiliere)
FORMATEUR (#numFormateur, nomFormateur, specialiteForm)
SESSION (#codeSess, dateDebutSess, dateFinSess, lieuSess,
*numFormateur → FORMATEUR.numFormateur,
*codeForm → FORMATION.codeForm)
ETUDIANT (#numCINEtu, nomEtu, prenomEtu, dateNaissEtu,
villeEtu, telEtu)
INSCRIPTION (#numCINEtu → ETUDIANT.numCINEtu,
#codeSess → SESSION.codeSess,
noteInscription)
CLÉ PRIMAIRE : (numCINEtu, codeSess)
FILIERE ───────▶ FORMATION ──────▶ SESSION ◄────── FORMATEUR
┃
INSCRIPTION
┃
ETUDIANT
Synthèse Merise
Récapitulatif visuel de la démarche complète CDC → RG → DD → Graphe DF → MCD → MLD, exercice de synthèse sur un cas inédit (bibliothèque), et tableau récapitulatif de tous les outils Merise.
- La démarche complète en 6 étapes : CDC → RG → DD → Graphe DF → MCD → MLD
- Les outils de chaque étape et leurs résultats
- Les pièges à éviter à chaque étape
- Application sur un cas inédit — bibliothèque municipale
┌──────────────────────────────────────────────────────────────────────────────┐
│ ① CDC │ ② RG │ ③ DD │ ④ GRAPHE DF │
├────────────────────┤ ├────────────────────┤ ├────────────────────┤ ├────────────────────┤
│ Périmètre │ RG1, RG2... │ Code/Type/ │ Sources = │
│ Entités cand. │ Verbes+quant.│ Taille/Obs. │ identifiants │
│ Limites │ Cardinalités │ objetConcept │ DF directes │
│ │ initiales │ Contrôles │ pas transitiv│
└────────────────────┘ └────────────────────┘ └────────────────────┘ └────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────────────────────────────────────────┐
│ ⑤ MCD │ ⑥ MLD │
├────────────────────────────────────────────┤
│ Entités + attributs (R1+R2) │ Tables + clés PK (R1) │
│ Associations + cardinalités │ FK migrées (R2, cardmax=1) │
│ R3 (porteuse), R4 (non-port.) │ Tables pivot (R3, N,N) │
│ R5 (hors graphe) │ Normalisation vérifiée │
└────────────────────────────────────────────┘
| Étape | Outil | Input | Output | Piège principal |
|---|---|---|---|---|
| 1. CDC | Entretiens, analyse | Besoins client | Document texte structuré | Oublier de fixer le périmètre |
| 2. RG | Lecture CDC, soulignage | CDC | Tableau RGn | Confondre RG métier et contrainte technique |
| 3. DD | Structure 5 colonnes | CDC + RG | Dictionnaire de données | Inclure des données calculées ou des synonymes |
| 4. Graphe DF | Analyse DF directes | DD + RG | Graphe orienté | Garder les DF transitives |
| 5. MCD | Règles R1→R5 | Graphe DF + RG | Entités + Associations | Oublier les cardinalités ou les attributs portés |
| 6. MLD | Règles MLD R1→R4 | MCD | Tables + FK | Confondre R2 (card max=1) et R3 (N,N) |
CDC — Bibliothèque Municipale de Rabat
« La bibliothèque gère des ADHERENTS identifiés par un numéro unique.
Elle possède un catalogue de LIVRES, chaque livre identifié par son ISBN.
Un livre peut exister en plusieurs EXEMPLAIRES physiques (un numéro par exemplaire).
Un adhérent peut emprunter plusieurs exemplaires simultanément.
Un exemplaire ne peut être emprunté que par un seul adhérent à la fois.
Chaque emprunt a une date de début et une date de retour prévue.
Chaque livre appartient à une seule CATEGORIE (Roman, Sciences, Histoire...).
Un auteur peut avoir écrit plusieurs livres, et un livre peut avoir plusieurs AUTEURS. »
Travail demandé :
1. Identifier les entités candidates
2. Rédiger les RG (RG1 à RG5)
3. Construire le DD (colonnes essentielles)
4. Dessiner le graphe des DF
5. Dessiner le MCD avec les cardinalités
6. Déduire le MLD
ENTITES : ADHERENT, LIVRE, EXEMPLAIRE, CATEGORIE, AUTEUR
REGLES DE GESTION :
RG1 : Un adhérent peut emprunter 0 ou plusieurs exemplaires. Un exemplaire est
emprunté par 0 ou 1 adhérent à la fois.
RG2 : Un livre possède 1 ou plusieurs exemplaires. Un exemplaire appartient à 1 seul livre.
RG3 : Un livre appartient à 1 seule catégorie. Une catégorie regroupe 0 ou plusieurs livres.
RG4 : Un livre a 1 ou plusieurs auteurs. Un auteur a écrit 0 ou plusieurs livres.
MCD :
ADHERENT (0,N) ◆ EMPRUNT (date_debut, date_retour_prevue) ◆ (0,1) EXEMPLAIRE
EXEMPLAIRE (1,N) ◆ POSSEDE ◆ (1,1) LIVRE
LIVRE (1,N) ◆ APPARTIENT ◆ (1,1) CATEGORIE
LIVRE (0,N) ◆ REDIGE ◆ (1,N) AUTEUR
MLD (6 tables) :
CATEGORIE(#codeCateg, libelleCateg)
AUTEUR(#idAuteur, nomAuteur, prenomAuteur)
LIVRE(#isbn, titreL, anneePubli, *codeCateg FK)
LIVRE_AUTEUR(#isbn FK, #idAuteur FK) ← R3 (N,N)
EXEMPLAIRE(#numExemp, etat, *isbn FK)
EMPRUNT(#idAdherent FK, #numExemp FK, dateDebut, dateRetourPrevue)
ADHERENT(#idAdherent, nomAdh, prenomAdh, emailAdh)
MySQL Workbench — Installation & Interface
Présentation de MySQL Workbench, ses 6 fonctionnalités principales, installation en 6 étapes sur Windows, découverte de l'interface d'accueil (3 modules) et configuration d'une connexion locale vers MySQL Server.
- Définir MySQL Workbench et le distinguer de MySQL Server
- Connaître les 6 fonctionnalités : connexion, modélisation EER, SQL, migration, reverse engineering, administration
- Installer Workbench en 6 étapes sur Windows
- Naviguer dans les 3 modules de l'interface d'accueil
- Configurer et tester une connexion locale (localhost:3306)
MySQL Server est le moteur de base de données — il stocke, traite et répond aux requêtes SQL. C'est un service qui tourne en arrière-plan. MySQL Workbench est l'interface graphique (GUI) qui permet d'interagir avec MySQL Server sans ligne de commande — dessiner des modèles, écrire des requêtes, gérer les utilisateurs. Workbench sans Server = voiture sans moteur.
| # | Fonctionnalité | Description | Usage SkolaBase |
|---|---|---|---|
| 1 | SQL Editor | Écrire, exécuter et analyser des requêtes SQL | Créer les tables, insérer les données, lancer les requêtes |
| 2 | Modélisation EER | Dessiner le schéma graphique des tables et relations | Traduire le MLD SkolaBase en diagramme visuel |
| 3 | Reverse Engineering | Générer le diagramme EER depuis une BDD existante | Visualiser une BDD importée automatiquement |
| 4 | Forward Engineering | Générer le script SQL CREATE TABLE depuis le modèle EER | Exporter le diagramme SkolaBase en SQL prêt à exécuter |
| 5 | Administration | Gérer utilisateurs, privilèges, sauvegardes | Créer l'utilisateur skolabase_user |
| 6 | Import/Export | Importer un fichier SQL ou exporter une BDD | Importer un dump .sql, exporter un CSV |
① Télécharger MySQL Installer
URL : https://dev.mysql.com/downloads/installer/
Choisir : "MySQL Installer for Windows" (mysql-installer-community-x.x.x.msi)
Taille : ~450 Mo (community = version gratuite)
② Lancer l'installeur en tant qu'Administrateur
Clic droit sur le .msi → "Exécuter en tant qu'administrateur"
③ Choisir le type d'installation
Sélectionner "Custom" pour choisir les composants
Cocher : MySQL Server 8.0 + MySQL Workbench 8.0
(Éviter "Full" — installe des composants inutiles)
④ Configurer MySQL Server
Port : 3306 (laisser par défaut)
Root password : choisir un mot de passe FORT (noter le !)
Windows Service : cocher "Start at system startup"
⑤ Exécuter l'installation
Cliquer "Execute" → attendre la fin de l'installation des deux composants
⑥ Vérifier l'installation
Démarrer MySQL Workbench → l'interface d'accueil doit s'ouvrir
Dans la liste des connexions : "Local instance MySQL80" doit apparaître
Oublier le mot de passe root : impossible de se connecter. Solution : noter le mot de passe dans un endroit sûr immédiatement. Port 3306 déjà utilisé : une autre instance MySQL tourne déjà. Vérifier dans Services Windows (services.msc) et arrêter l'ancienne instance. Antivirus bloquant : désactiver temporairement l'antivirus pendant l'installation.
┌──────────────────────────────────────────────────────────┐
│ MySQL Workbench — Home Screen │
├──────────────────────────────────────────────────────────┤
│ MODULE 1 : MySQL Connections │
│ ┌─────────────────┐ ┌─────────────┐ │
│ │ Local MySQL80 │ │ + New conn. │ │
│ │ localhost:3306 │ └─────────────┘ │
│ └─────────────────┘ │
├──────────────────────────────────────────────────────────┤
│ MODULE 2 : Models (EER Diagrams) │
│ ┌────────────────┐ │
│ │ + New Model │ Ouvrir un modèle .mwb existant │
│ └────────────────┘ │
├──────────────────────────────────────────────────────────┤
│ MODULE 3 : MySQL Migration Wizard │
│ Migrer depuis SQL Server, Access, PostgreSQL vers MySQL │
└──────────────────────────────────────────────────────────┘
Configurer une nouvelle connexion
Home Screen → + (MySQL Connections) → remplir :
- Connection Name :
SkolaBase_Local - Hostname :
127.0.0.1oulocalhost - Port :
3306 - Username :
root - Cliquer Test Connection → saisir le mot de passe root → « Connection parameters are correct »
- Cliquer OK → la connexion apparaît sur le Home Screen
Créer un modèle EER dans Workbench
Créer un nouveau modèle EER, configurer le schéma, ajouter des tables avec leurs colonnes (nom, datatype, PK, NN, UQ, AI), créer les tables SkolaBase graphiquement et préparer le Forward Engineering.
- Créer un nouveau modèle via File → New Model
- Configurer le nom du schéma (skolabase)
- Ajouter une table et configurer ses colonnes dans l'éditeur
- Maîtriser les cases à cocher : PK, NN, UQ, BIN, UN, ZF, AI, G
- Placer les tables sur le diagramme EER
- Générer le script SQL via Forward Engineering
① File → New Model (ou Ctrl+N depuis le module Models)
② Configurer le schéma :
Onglet "EER Diagram" → Double-clic sur "mydb" dans le panneau de gauche
→ Name : skolabase
→ Default Charset : utf8mb4
→ Default Collation : utf8mb4_unicode_ci
③ Ouvrir le diagramme EER :
Double-clic sur "Add Diagram" (icône diagramme) dans le panneau gauche
→ Un canvas blanc s'ouvre — c'est ici qu'on dessine
④ Sauvegarder le modèle :
File → Save Model (Ctrl+S)
→ Nom du fichier : SkolaBase.mwb (format propriétaire Workbench)
→ Sauvegarder régulièrement !
① Dans la barre d'outils du diagramme EER :
Cliquer l'icône "Table" (rectangle avec colonnes)
→ Cliquer sur le canvas pour placer la table
② Nommer la table :
Double-clic sur la table placée → onglet "Table"
→ Name : Etudiant
→ Engine : InnoDB (toujours InnoDB pour les FK)
→ Charset/Collation : utf8mb4 / utf8mb4_unicode_ci
③ Ajouter les colonnes (onglet "Columns") :
Cliquer dans la zone vide sous les en-têtes pour ajouter une ligne
Column Name | Datatype | PK | NN | UQ | AI | Default
numCINEtu | VARCHAR(8) | ✓ | ✓ | | |
nomEtu | VARCHAR(50) | | ✓ | | |
prenomEtu | VARCHAR(50) | | ✓ | | |
dateNaissEtu | DATE | | | | | NULL
villeEtu | VARCHAR(50) | | ✓ | | |
telEtu | VARCHAR(15) | | | | | NULL
| Case | Sigle | Signification | Effet SQL généré |
|---|---|---|---|
| PK | Primary Key | Clé primaire — identifiant unique de la ligne | PRIMARY KEY, implique NN automatiquement |
| NN | Not Null | La colonne ne peut pas être vide | NOT NULL |
| UQ | Unique | Toutes les valeurs doivent être différentes | UNIQUE |
| BIN | Binary | Stockage binaire (sensible à la casse) | BINARY |
| UN | Unsigned | Entier positif uniquement (pas de négatif) | UNSIGNED |
| ZF | Zero Fill | Compléter avec des zéros à gauche | ZEROFILL |
| AI | Auto Increment | Incrémentation automatique à chaque INSERT | AUTO_INCREMENT |
| G | Generated | Colonne calculée automatiquement | GENERATED ALWAYS AS |
AI est utile quand la clé primaire est un entier généré automatiquement par MySQL (numFormateur, numAdhérent...). Pour les clés métier (numCINEtu = donnée réelle fournie par l'utilisateur), on n'utilise pas AI — la valeur est saisie manuellement.
TABLE : Filiere
codeFiliere VARCHAR(6) PK NN
libelleFiliere VARCHAR(80) NN
TABLE : Formation
codeForm VARCHAR(10) PK NN
titreForm VARCHAR(100) NN
dureeForm SMALLINT NN
niveauForm VARCHAR(20)
codeFiliere VARCHAR(6) NN ← FK vers Filiere (à configurer en section suivante)
TABLE : Formateur
numFormateur INT PK NN AI
nomFormateur VARCHAR(50) NN
specialiteForm VARCHAR(80)
TABLE : Session
codeSess VARCHAR(10) PK NN
dateDebutSess DATE NN
dateFinSess DATE
lieuSess VARCHAR(100)
numFormateur INT NN ← FK vers Formateur
codeForm VARCHAR(10) NN ← FK vers Formation
TABLE : Etudiant
numCINEtu VARCHAR(8) PK NN
nomEtu VARCHAR(50) NN
prenomEtu VARCHAR(50) NN
dateNaissEtu DATE
villeEtu VARCHAR(50) NN
telEtu VARCHAR(15)
TABLE : Inscription
numCINEtu VARCHAR(8) PK NN ← FK vers Etudiant
codeSess VARCHAR(10) PK NN ← FK vers Session
noteInscription DECIMAL(4,2) ← entre 0.00 et 20.00
① Database → Forward Engineer...
② Connexion : choisir SkolaBase_Local → Next
③ Options : cocher "DROP objects before each CREATE" → Next
④ Objets : sélectionner toutes les tables → Next
⑤ Prévisualiser le SQL généré → Next → Execute
⑥ Résultat : toutes les tables créées dans MySQL !
Inscription a deux colonnes cochées PK : numCINEtu et codeSess. Quel type de clé primaire Workbench génère-t-il ?Relations & associations graphiques dans Workbench
Créer des relations entre tables dans le diagramme EER — types de relations (Identifying vs Non-Identifying), cardinalités Workbench, clés étrangères générées automatiquement, notation graphique et diagramme complet SkolaBase.
- Distinguer Identifying Relationship et Non-Identifying Relationship
- Utiliser les 6 outils de relation de la barre d'outils EER
- Lire la notation Workbench (patte de corbeau, tiret, cercle)
- Vérifier les FK générées automatiquement dans l'onglet Foreign Keys
- Configurer ON DELETE et ON UPDATE sur chaque FK
- Obtenir le diagramme EER complet SkolaBase
Une Identifying Relationship (ligne pleine) : l'entité enfant ne peut pas exister sans l'entité parent. La FK fait partie de la clé primaire de l'enfant. Exemple : une INSCRIPTION ne peut pas exister sans un ETUDIANT.
Une Non-Identifying Relationship (ligne pointillée) : l'entité enfant peut exister indépendamment. La FK n'est pas dans la PK. Exemple : une SESSION peut exister sans FORMATION (si on retarde l'affectation).
| Critère | Identifying Relationship | Non-Identifying Relationship |
|---|---|---|
| Représentation | Ligne pleine | Ligne pointillée |
| FK dans la PK | Oui — la FK fait partie de la clé primaire | Non — la FK est une colonne ordinaire |
| Existence enfant | Impossible sans le parent | Possible indépendamment du parent |
| Exemples SkolaBase | INSCRIPTION (contient PK de ETUDIANT + SESSION) | SESSION → FORMATEUR, FORMATION → FILIERE |
| Cardinalités typiques | Association N:M (table pivot) | 1:N (FK dans la table « côté N ») |
| Outil | Type | Usage | SkolaBase |
|---|---|---|---|
| —1:1 (Identifying) | Un-à-un, identifying | Rare — entité faible 1:1 | Non utilisé |
| —1:N (Identifying) | Un-à-plusieurs, identifying | FK dans PK de la table enfant | INSCRIPTION → ETUDIANT et SESSION |
| ──1:1 (Non-Id) | Un-à-un, non-identifying | Entités indépendantes 1:1 | Non utilisé |
| ──1:N (Non-Id) | Un-à-plusieurs, non-identifying | FK ordinaire dans la table enfant | SESSION → FORMATEUR, SESSION → FORMATION |
| ──N:M (Non-Id) | Plusieurs-à-plusieurs | Génère une table pivot automatiquement | Alternative à créer INSCRIPTION manuellement |
| ──N:M (Identifying) | Plusieurs-à-plusieurs, identifying | Table pivot avec FK dans la PK | INSCRIPTION (méthode recommandée) |
Symboles en bout de ligne (notation Crow's Foot) :
│ = exactement 1 (obligatoire, maximum 1)
o = 0 (facultatif)
< = N (plusieurs, patte de corbeau)
Combinaisons :
││ = (1,1) exactement un Formateur ─││─ Session : 1 formateur/session
o│ = (0,1) zéro ou un Session ─│o── Formateur : un formateur peut avoir 0 session
│< = (1,N) au moins un Filiere ─<│── Formation : une formation/une filière
o< = (0,N) zéro ou plusieurs Etudiant ─<o─ Inscription : 0 ou N inscriptions/étudiant
① Sélectionner l'outil "1:N Non-Identifying Relationship" dans la barre d'outils
② Cliquer sur la table Formation (côté N — plusieurs formations/filière)
puis cliquer sur la table Filiere (côté 1 — une filière par formation)
③ Workbench ajoute automatiquement dans Formation :
→ Colonne codeFiliere VARCHAR(6) NN
→ Contrainte FK : Formation_ibfk_1 REFERENCES Filiere(codeFiliere)
④ Configurer la FK (double-clic sur la ligne de relation) :
Onglet "Foreign Key"
→ Renommer : fk_Formation_Filiere (plus lisible)
→ ON DELETE : RESTRICT (impossible de supprimer une filière si des formations y sont rattachées)
→ ON UPDATE : CASCADE (si codeFiliere change, répercuter automatiquement)
Toutes les relations SkolaBase :
Filiere (1) ─── (N) Formation → Non-Identifying 1:N ON DELETE RESTRICT
Formation(1) ─── (N) Session → Non-Identifying 1:N ON DELETE RESTRICT
Formateur(1) ─── (N) Session → Non-Identifying 1:N ON DELETE RESTRICT
Etudiant (N) ─── (N) Session via Inscription → Identifying 1:N sur les deux côtés
| ON DELETE | Comportement | Usage SkolaBase |
|---|---|---|
RESTRICT | Interdit la suppression du parent si des enfants existent | Filiere → Formation : impossible de supprimer INF s'il y a des formations |
CASCADE | Supprime automatiquement les enfants quand le parent est supprimé | Etudiant → Inscription : supprimer un étudiant supprime ses inscriptions |
SET NULL | Met la FK à NULL dans les enfants | Formateur → Session : si formateur supprimé, session sans formateur (NULL) |
NO ACTION | Identique à RESTRICT sous MySQL/InnoDB | Synonyme de RESTRICT en pratique |
Inscription.numCINEtu → Etudiant.numCINEtu. Que se passe-t-il si on supprime l'étudiant A123456 ?MySQL Server — Installation & Gestion
MySQL 8.0 — types d'installation, configuration du port 3306, gestion des services Windows et Linux, vérification avec SHOW VARIABLES, comptes initiaux et bonnes pratiques de sécurité.
- Comprendre le rôle de MySQL Server comme moteur SGBDR
- Choisir entre les types d'installation (Developer Default, Server Only, Custom)
- Configurer le port 3306, le mode d'authentification et le mot de passe root
- Démarrer, arrêter et vérifier MySQL Server sous Windows et Linux
- Exécuter les premières commandes :
SHOW VARIABLES,SHOW DATABASES,STATUS
MySQL Server est le moteur de base de données relationnelle qui stocke, gère et répond aux requêtes SQL. C'est un processus système (service) qui tourne en arrière-plan et écoute sur le port 3306. Sans MySQL Server actif, aucune application (Workbench, PHP, Python...) ne peut accéder aux données.
| Type d'installation | Composants installés | Usage recommandé |
|---|---|---|
| Developer Default | Server + Workbench + Shell + Connecteurs (Python, Java, .NET...) | Développeur full-stack — tout en un |
| Server Only | MySQL Server uniquement | Serveur de production — sans interface graphique |
| Client Only | Outils clients (Workbench, Shell) sans le serveur | Poste qui se connecte à un serveur distant |
| Custom | Choisir précisément les composants | Contrôle total — recommandé pour les TP |
| Full | Tout — Server + Workbench + tous les connecteurs | Éviter — installe des composants inutiles |
① Type and Networking
Config Type : Development Computer (réserve moins de mémoire)
Port : 3306 (ne pas changer sauf conflit)
Named Pipe : désactiver (inutile pour notre usage)
Open Windows Firewall port : cocher (accès réseau local)
② Authentication Method
Choisir : "Use Strong Password Encryption" (caching_sha2_password)
→ MySQL 8.0 par défaut — plus sécurisé que l'ancien mysql_native_password
Attention : certains vieux pilotes PHP/Python ne supportent pas encore ce mode
→ Si problèmes de connexion PHP : choisir "Use Legacy Authentication Method"
③ Accounts and Roles
Root Password : choisir un mot de passe FORT (minimum 12 caractères)
Exemple : SkolaBase_2024! ← à noter impérativement
Ajouter un compte : optionnel — on créera les utilisateurs après (Partie 9)
④ Windows Service
Service Name : MySQL80 (défaut)
Start at Startup: cocher → MySQL démarre automatiquement avec Windows
Run as System Account : garder par défaut
Le compte root a tous les droits sur toutes les bases de données. En production, il ne faut jamais utiliser root pour les applications — créer un utilisateur dédié avec les droits minimaux nécessaires (Partie 9). En développement local, root est acceptable pour les TP.
# Démarrer MySQL
net start MySQL80
# Arrêter MySQL
net stop MySQL80
# Vérifier le statut
sc query MySQL80
# Résultat attendu : STATE : 4 RUNNING
# Méthode graphique : services.msc
# Chercher MySQL80 → Clic droit → Démarrer/Arrêter/Redémarrer
# Démarrer MySQL
sudo systemctl start mysql
# Arrêter MySQL
sudo systemctl stop mysql
# Redémarrer (après modification my.cnf)
sudo systemctl restart mysql
# Vérifier le statut
sudo systemctl status mysql
# Résultat attendu : Active: active (running)
# Activer le démarrage automatique
sudo systemctl enable mysql
-- Vérifier la version et l'état
STATUS;
-- Affiche : Server version: 8.0.xx, Connection id, Uptime...
-- Variables de configuration importantes
SHOW VARIABLES LIKE 'version';
SHOW VARIABLES LIKE 'port';
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'max_connections';
-- Bases de données présentes par défaut
SHOW DATABASES;
-- information_schema : métadonnées sur toutes les BDD
-- mysql : comptes utilisateurs, privilèges
-- performance_schema : métriques de performance
-- sys : vues simplifiées de performance_schema
SHOW DATABASES affiche 4 bases de données (information_schema, mysql, performance_schema, sys). Peut-on supprimer la base mysql pour libérer de l'espace ?SGBD & SQL — Panorama
Définition d'un SGBD et ses fonctions, architecture ANSI/SPARC 3 niveaux, catégories SQL (LDD/LMD/LQD/LCD), moteurs MySQL (InnoDB/MyISAM/Memory), et gestion des bases de données (CREATE/USE/DROP DATABASE).
- Définir un SGBD et ses 5 fonctions fondamentales
- Architecture ANSI/SPARC — 3 niveaux d'abstraction
- Les 4 catégories SQL : LDD, LMD, LQD, LCD
- Moteurs de stockage MySQL : InnoDB vs MyISAM vs Memory
- Commandes BDD :
CREATE DATABASE,USE,DROP DATABASE,SHOW DATABASES
Un Système de Gestion de Base de Données (SGBD) est un logiciel qui assure la définition, la manipulation, la sécurisation et le partage des données. MySQL est un SGBDR (Relationnel) — il organise les données en tables liées par des clés. Autres exemples : PostgreSQL, Oracle, SQL Server (SGBDR), MongoDB, Cassandra (NoSQL).
| # | Fonction | Description | Exemple MySQL |
|---|---|---|---|
| 1 | Définition | Décrire la structure des données (schéma) | CREATE TABLE, ALTER TABLE |
| 2 | Manipulation | Insérer, modifier, supprimer, interroger | INSERT, UPDATE, DELETE, SELECT |
| 3 | Sécurité | Contrôler les accès selon les rôles | GRANT, REVOKE, CREATE USER |
| 4 | Intégrité | Garantir la cohérence et la validité des données | Clés étrangères, NOT NULL, CHECK |
| 5 | Partage | Permettre l'accès simultané et gérer les conflits | Transactions, verrous, ACID |
NIVEAU 1 — EXTERNE (Vues utilisateurs)
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Vue Etudiant │ │ Vue Admin │ │ Vue Rapport │
└───────────┘ └───────────┘ └───────────┘
│ │ │
└──────────────────────┴──────────────────────┘
│
NIVEAU 2 — CONCEPTUEL (Schéma logique global)
┌─────────────────────────────────────────────┐
│ Tables : Etudiant, Session, Inscription... │
│ Relations, contraintes, types de données │
└─────────────────────────────────────────────┘
│
NIVEAU 3 — INTERNE (Stockage physique)
┌─────────────────────────────────────────────┐
│ Fichiers .ibd, index B-Tree, pages 16Ko │
│ Géré par MySQL InnoDB — transparent │
└─────────────────────────────────────────────┘
Indépendance logique : changer le schéma ne modifie pas les vues utilisateurs
Indépendance physique : changer le stockage ne modifie pas le schéma
| Catégorie | Nom complet | Commandes | Action |
|---|---|---|---|
| LDD | Langage de Définition de Données (DDL) | CREATE, ALTER, DROP, TRUNCATE, RENAME | Définir et modifier la structure (tables, colonnes, index) |
| LMD | Langage de Manipulation de Données (DML) | INSERT, UPDATE, DELETE | Insérer, modifier, supprimer des données |
| LQD | Langage de Quête de Données (DQL) | SELECT | Interroger et extraire les données |
| LCD | Langage de Contrôle de Données (DCL) | GRANT, REVOKE, CREATE USER | Gérer les accès et les permissions |
Certains auteurs ajoutent le TCL (Transaction Control Language) : COMMIT, ROLLBACK, SAVEPOINT. Ces commandes valident ou annulent des groupes d'opérations LMD pour garantir la cohérence ACID.
| Moteur | Transactions | Clés étrangères | Performances | Usage recommandé |
|---|---|---|---|---|
| InnoDB | Oui — ACID | Oui — intégrité référentielle | Équilibrées | Par défaut — toujours utiliser pour les tables avec FK |
| MyISAM | Non | Non | Lecture rapide | Tables en lecture seule, archives (déprécié) |
| Memory | Non | Non | Très rapide (RAM) | Tables temporaires, caches — données perdues au redémarrage |
-- Créer la base de données SkolaBase
CREATE DATABASE skolabase
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- utf8mb4 : supporte les caractères arabes, accents, emojis
-- utf8mb4_unicode_ci : comparaisons insensibles à la casse
-- Sélectionner la base active (toutes les commandes suivantes s'y appliquent)
USE skolabase;
-- Vérifier la base active
SELECT DATABASE();
-- Résultat : skolabase
-- Lister toutes les bases disponibles
SHOW DATABASES;
-- Lister les tables de la base active
SHOW TABLES;
-- Supprimer une base (ATTENTION : irréversible, supprime toutes les tables)
DROP DATABASE IF EXISTS skolabase_test;
-- IF EXISTS évite une erreur si la base n'existe pas
-- Afficher la structure de création d'une base
SHOW CREATE DATABASE skolabase;
MyISAM au lieu de InnoDB. Que se passe-t-il quand il ajoute une clé étrangère avec FOREIGN KEY REFERENCES ?CREATE DATABASE skolabase CHARACTER SET utf8mb4 vs CREATE DATABASE skolabase CHARACTER SET utf8 — quelle est la différence pratique pour SkolaBase ?CREATE TABLE & Types de données MySQL
Syntaxe complète de CREATE TABLE, panorama des types de données (numériques, chaînes de caractères, date/heure), choisir le bon type pour chaque attribut de SkolaBase, et créer les 6 tables en SQL pur.
- Syntaxe complète
CREATE TABLEavec tous les éléments - Types numériques :
TINYINT,SMALLINT,INT,BIGINT,DECIMAL,FLOAT - Types chaînes :
CHAR,VARCHAR,TEXT,ENUM - Types date/heure :
DATE,DATETIME,TIMESTAMP,TIME,YEAR - Choisir le bon type : critères de taille, performance et sémantique
- Créer les 6 tables SkolaBase avec les bons types
CREATE TABLE [IF NOT EXISTS] nom_table (
nom_colonne type_donnee [contraintes_colonne],
nom_colonne type_donnee [contraintes_colonne],
...
[contraintes_table]
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci
COMMENT = 'Description de la table';
-- Contraintes colonne : NOT NULL, DEFAULT valeur, UNIQUE, AUTO_INCREMENT
-- Contraintes table : PRIMARY KEY(...), FOREIGN KEY(...) REFERENCES ...,
-- INDEX, UNIQUE KEY, CHECK
CREATE TABLE IF NOT EXISTS Formateur (
numFormateur INT NOT NULL AUTO_INCREMENT,
nomFormateur VARCHAR(50) NOT NULL,
prenomFormateur VARCHAR(50) NOT NULL,
specialiteForm VARCHAR(80) NULL,
emailFormateur VARCHAR(100) NULL,
actifFormateur BOOLEAN NOT NULL DEFAULT TRUE,
PRIMARY KEY (numFormateur)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COMMENT = 'Formateurs du centre SkolaBase';
| Type | Octets | Plage (signé) | Usage SkolaBase | Conseil |
|---|---|---|---|---|
TINYINT | 1 | -128 à 127 | actifFormateur (0/1) | BOOLEAN = alias TINYINT(1) |
SMALLINT | 2 | -32 768 à 32 767 | dureeForm (0—9999h) | Durées courtes, compteurs |
INT | 4 | -2,1 Md à 2,1 Md | numFormateur, clés PK AI | PK auto-incrément par défaut |
BIGINT | 8 | ±9,2 × 1018 | ID très grande table | Seulement si > 2 milliards de lignes |
DECIMAL(p,s) | Variable | p chiffres, s décimales | noteInscription DECIMAL(4,2) | Précision exacte — finances, notes |
FLOAT | 4 | ±3,4 × 1038 | Statistiques, moyennes | Approximatif — jamais pour les finances |
DOUBLE | 8 | ±1,8 × 10308 | Calculs scientifiques | Très approximatif — préférer DECIMAL |
FLOAT est approximatif : SELECT 14.50 + 5.50 peut donner 19.9999999.... DECIMAL(4,2) est exact : 14.50 + 5.50 = 20.00 garanti. Pour les notes, montants financiers et toute valeur qui doit être exacte, toujours utiliser DECIMAL.
| Type | Taille | Stockage | Usage SkolaBase | Conseil |
|---|---|---|---|---|
CHAR(n) | n caractères fixes | n octets (complété d'espaces) | codeFiliere CHAR(6) | Codes fixes (INSEE, CIN, codes postaux) |
VARCHAR(n) | 1 à n caractères | longueur réelle + 1–2 octets | nomEtu VARCHAR(50) | Noms, titres, textes courts variables |
TEXT | 65 535 caractères | Hors ligne (lent) | description longue | Éviter pour les colonnes indexées |
MEDIUMTEXT | 16 Mo | Hors ligne | Contenu long (articles) | Rarement nécessaire pour SkolaBase |
ENUM('v1','v2') | Liste de valeurs | 1–2 octets | niveauForm ENUM('Bac','Bac+2','Bac+3') | Valeurs prédéfinies et limitées |
CHAR(8) stocke toujours 8 caractères (complète avec des espaces). Parfait pour des codes de longueur fixe comme les numéros CIN marocains (lettre + 7 chiffres, ex : A1234567). VARCHAR(50) stocke la longueur réelle. Parfait pour les noms : Alaoui (6 caractères) vs Bensouda-Korachi (16 caractères) — pas de gaspillage d'espace.
| Type | Format | Plage | Usage SkolaBase |
|---|---|---|---|
DATE | AAAA-MM-JJ | 1000-01-01 à 9999-12-31 | dateNaissEtu, dateDebutSess, dateFinSess |
DATETIME | AAAA-MM-JJ HH:MM:SS | 1000-01-01 à 9999-12-31 | création/modification enregistrements |
TIMESTAMP | AAAA-MM-JJ HH:MM:SS | 1970-01-01 à 2038-01-19 | created_at, updated_at (mis à jour auto) |
TIME | HHH:MM:SS | -838:59:59 à 838:59:59 | heureDebut, heureFin (horaires) |
YEAR | AAAA | 1901 à 2155 | annéeScolaire |
Le type TIMESTAMP avec l'option DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP se met à jour automatiquement à chaque modification de la ligne. Très utile pour tracer les changements sans code applicatif supplémentaire.
USE skolabase;
CREATE TABLE IF NOT EXISTS Filiere (
codeFiliere CHAR(6) NOT NULL,
libelleFiliere VARCHAR(80) NOT NULL,
PRIMARY KEY (codeFiliere)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS Formation (
codeForm VARCHAR(10) NOT NULL,
titreForm VARCHAR(100) NOT NULL,
dureeForm SMALLINT NOT NULL COMMENT 'Durée en heures',
niveauForm ENUM('Technicien','Bac','Bac+2','Bac+3') NULL,
codeFiliere CHAR(6) NOT NULL,
PRIMARY KEY (codeForm),
CONSTRAINT fk_Form_Filiere
FOREIGN KEY (codeFiliere) REFERENCES Filiere(codeFiliere)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS Formateur (
numFormateur INT NOT NULL AUTO_INCREMENT,
nomFormateur VARCHAR(50) NOT NULL,
prenomFormateur VARCHAR(50) NOT NULL,
specialiteForm VARCHAR(80) NULL,
PRIMARY KEY (numFormateur)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS Session (
codeSess VARCHAR(10) NOT NULL,
dateDebutSess DATE NOT NULL,
dateFinSess DATE NULL,
lieuSess VARCHAR(100) NULL,
numFormateur INT NOT NULL,
codeForm VARCHAR(10) NOT NULL,
PRIMARY KEY (codeSess),
CONSTRAINT fk_Sess_Formateur
FOREIGN KEY (numFormateur) REFERENCES Formateur(numFormateur)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_Sess_Formation
FOREIGN KEY (codeForm) REFERENCES Formation(codeForm)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS Etudiant (
numCINEtu CHAR(8) NOT NULL COMMENT 'Lettre + 7 chiffres ex: A1234567',
nomEtu VARCHAR(50) NOT NULL,
prenomEtu VARCHAR(50) NOT NULL,
dateNaissEtu DATE NULL,
villeEtu VARCHAR(50) NOT NULL DEFAULT 'Casablanca',
telEtu VARCHAR(15) NULL,
PRIMARY KEY (numCINEtu)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS Inscription (
numCINEtu CHAR(8) NOT NULL,
codeSess VARCHAR(10) NOT NULL,
noteInscription DECIMAL(4,2) NULL COMMENT 'Note sur 20, ex: 14.50',
PRIMARY KEY (numCINEtu, codeSess),
CONSTRAINT fk_Insc_Etudiant
FOREIGN KEY (numCINEtu) REFERENCES Etudiant(numCINEtu)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_Insc_Session
FOREIGN KEY (codeSess) REFERENCES Session(codeSess)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
FLOAT et un autre propose DECIMAL(4,2). Lequel choisir et pourquoi ?CHAR(8) est utilisé pour numCINEtu. Que stocke MySQL si on insère la valeur 'A123456' (7 caractères au lieu de 8) ?Contraintes d'intégrité
Les 3 types de contraintes (domaine, entité, référentielle), syntaxe complète de PRIMARY KEY, FOREIGN KEY (ON DELETE/UPDATE), NOT NULL, DEFAULT, UNIQUE, CHECK (ENFORCED), et ajout de contraintes via ALTER TABLE.
- Les 3 types de contraintes : domaine, entité, référentielle
PRIMARY KEY— simple et composéeFOREIGN KEY— ON DELETE et ON UPDATE (CASCADE, RESTRICT, SET NULL, NO ACTION)NOT NULL,DEFAULT,UNIQUECHECKavecENFORCED— vérifier les plages de valeurs- Ajouter des contraintes sur une table existante avec
ALTER TABLE
Sans contraintes, rien n'empêche d'insérer une note de 150, un étudiant sans nom, ou une inscription pour un étudiant inexistant. Les contraintes garantissent que les données restent cohérentes et valides — elles implémentent les règles de gestion directement dans la BDD, indépendamment des applications.
| Type | Définition | Contraintes SQL | Exemple SkolaBase |
|---|---|---|---|
| Contrainte de domaine | Limite les valeurs autorisées pour un attribut | NOT NULL, DEFAULT, CHECK, ENUM | noteInscription entre 0 et 20, niveauForm dans une liste |
| Contrainte d'entité | Garantit l'unicité et l'identification de chaque ligne | PRIMARY KEY, UNIQUE, NOT NULL | numCINEtu unique par étudiant, codeSess unique par session |
| Contrainte référentielle | Garantit la cohérence des liens entre tables | FOREIGN KEY ... REFERENCES | numFormateur dans Session doit exister dans Formateur |
-- Façon 1 : inline (colonne unique, syntaxe courte)
CREATE TABLE Filiere (
codeFiliere CHAR(6) NOT NULL PRIMARY KEY,
libelleFiliere VARCHAR(80) NOT NULL
);
-- Façon 2 : en fin de table (recommandé — plus lisible)
CREATE TABLE Formation (
codeForm VARCHAR(10) NOT NULL,
titreForm VARCHAR(100) NOT NULL,
codeFiliere CHAR(6) NOT NULL,
PRIMARY KEY (codeForm) -- PK simple
);
-- Façon 3 : avec nom de contrainte (bonne pratique)
CREATE TABLE Inscription (
numCINEtu CHAR(8) NOT NULL,
codeSess VARCHAR(10) NOT NULL,
note DECIMAL(4,2),
CONSTRAINT pk_Inscription PRIMARY KEY (numCINEtu, codeSess) -- PK composée nommée
);
-- Vérifier les clés d'une table
SHOW KEYS FROM Inscription;
-- ou :
SHOW CREATE TABLE Inscription;
Une PRIMARY KEY implique automatiquement : NOT NULL (pas de valeur nulle), UNIQUE (pas de doublon), et création d'un index B-Tree (accès rapide). Une table ne peut avoir qu'une seule PRIMARY KEY, mais elle peut porter sur plusieurs colonnes (clé composée).
CREATE TABLE Session (
codeSess VARCHAR(10) NOT NULL,
dateDebutSess DATE NOT NULL,
dateFinSess DATE NULL,
lieuSess VARCHAR(100) NULL,
numFormateur INT NOT NULL,
codeForm VARCHAR(10) NOT NULL,
PRIMARY KEY (codeSess),
-- FK nommée explicitement (bonne pratique)
CONSTRAINT fk_Sess_Formateur
FOREIGN KEY (numFormateur)
REFERENCES Formateur(numFormateur)
ON DELETE RESTRICT -- Interdit supprimer un formateur avec des sessions
ON UPDATE CASCADE, -- Si numFormateur change, répercuter dans Session
CONSTRAINT fk_Sess_Formation
FOREIGN KEY (codeForm)
REFERENCES Formation(codeForm)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- Vérifier les FK d'une table
SELECT
CONSTRAINT_NAME,
COLUMN_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'Session'
AND TABLE_SCHEMA = 'skolabase'
AND REFERENCED_TABLE_NAME IS NOT NULL;
| Option | ON DELETE | ON UPDATE | Usage SkolaBase |
|---|---|---|---|
RESTRICT | Bloque la suppression du parent si des enfants existent | Bloque la modification de la PK parent | Filiere→Formation : ne pas supprimer une filière si des formations y sont rattachées |
CASCADE | Supprime automatiquement les enfants | Propage la modification aux enfants | Etudiant→Inscription : supprimer un étudiant supprime ses inscriptions |
SET NULL | Met la FK à NULL dans les enfants | Met la FK à NULL | Si un formateur est supprimé, ses sessions gardent NULL comme formateur (la colonne doit accepter NULL) |
NO ACTION | Identique à RESTRICT sous InnoDB | Identique à RESTRICT sous InnoDB | Valeur par défaut si non spécifiée |
On ne peut pas créer une FK vers une table qui n'existe pas encore. Dans SkolaBase, l'ordre obligatoire est : Filiere → Formation → Formateur → Session → Etudiant → Inscription. Toujours créer les tables référencées avant celles qui les référencent.
CREATE TABLE Etudiant (
numCINEtu CHAR(8) NOT NULL, -- Obligatoire
nomEtu VARCHAR(50) NOT NULL, -- Obligatoire
prenomEtu VARCHAR(50) NOT NULL, -- Obligatoire
dateNaissEtu DATE NULL, -- Optionnel
villeEtu VARCHAR(50) NOT NULL DEFAULT 'Casablanca', -- Valeur par défaut
telEtu VARCHAR(15) NULL,
emailEtu VARCHAR(100) NULL UNIQUE, -- Unique (index automatique)
PRIMARY KEY (numCINEtu),
-- CHECK : vérifier le format CIN marocain (lettre + 7 chiffres)
CONSTRAINT chk_CIN
CHECK (numCINEtu REGEXP '^[A-Z][0-9]{7}$') ENFORCED,
-- CHECK : date de naissance réaliste
CONSTRAINT chk_DateNaiss
CHECK (dateNaissEtu IS NULL OR dateNaissEtu < CURDATE()) ENFORCED
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- CHECK sur la note dans Inscription
ALTER TABLE Inscription
ADD CONSTRAINT chk_Note
CHECK (noteInscription IS NULL
OR (noteInscription >= 0 AND noteInscription <= 20)) ENFORCED;
-- UNIQUE sur plusieurs colonnes (unicité composite)
ALTER TABLE Session
ADD CONSTRAINT uq_Sess_FormDate
UNIQUE (codeForm, dateDebutSess);
-- Un même formation ne peut pas avoir deux sessions au même jour
| Contrainte | Comportement si violée | Exemple |
|---|---|---|
NOT NULL | Erreur : Column 'nomEtu' cannot be null | INSERT sans nomEtu |
DEFAULT | Pas d'erreur — valeur par défaut utilisée | INSERT sans villeEtu → 'Casablanca' |
UNIQUE | Erreur : Duplicate entry 'x' for key 'emailEtu' | Deux étudiants même email |
CHECK | Erreur : Check constraint 'chk_Note' is violated | INSERT note = 25 |
FOREIGN KEY | Erreur : Cannot add or update a child row | INSERT inscription étudiant inexistant |
CHECK (noteInscription >= 0 AND noteInscription <= 20) est définie. On exécute INSERT INTO Inscription VALUES ('A1234567', 'SESS-01', NULL). Que se passe-t-il ?INF alors que 3 formations y sont rattachées. La FK est configurée ON DELETE RESTRICT. Que retourne MySQL ?ALTER TABLE & DROP TABLE
Modifier la structure d'une table existante avec ALTER TABLE (ADD, MODIFY, CHANGE, DROP, RENAME TO), gérer les index et clés étrangères, et supprimer tables et bases avec DROP TABLE et DROP DATABASE.
ALTER TABLE ... ADD COLUMN— ajouter une colonne avec position (AFTER/FIRST)ALTER TABLE ... MODIFY COLUMN— changer le type ou les contraintesALTER TABLE ... CHANGE COLUMN— renommer ET modifier une colonneALTER TABLE ... DROP COLUMN— supprimer une colonneALTER TABLE ... RENAME TO— renommer une tableDROP TABLE IF EXISTS— supprimer une table en toute sécurité- Ajouter et supprimer des index et des FK sur une table existante
-- ADD COLUMN : ajouter une colonne
ALTER TABLE Etudiant
ADD COLUMN emailEtu VARCHAR(100) NULL
AFTER telEtu; -- Positionner après telEtu
-- AFTER col : insérer après une colonne précise
-- FIRST : insérer en première position
-- (sans AFTER/FIRST : ajoutée en dernière position par défaut)
-- Ajouter plusieurs colonnes en une commande
ALTER TABLE Formateur
ADD COLUMN emailFormateur VARCHAR(100) NULL AFTER nomFormateur,
ADD COLUMN telFormateur VARCHAR(15) NULL AFTER emailFormateur,
ADD COLUMN actif BOOLEAN NOT NULL DEFAULT TRUE;
-- MODIFY COLUMN : changer le type ou les contraintes (garde le nom)
ALTER TABLE Formation
MODIFY COLUMN titreForm VARCHAR(150) NOT NULL; -- Agrandir la taille
-- Attention : peut échouer si les données existantes ne sont pas compatibles
ALTER TABLE Inscription
MODIFY COLUMN noteInscription DECIMAL(4,2) NOT NULL DEFAULT 0.00;
-- Ajouter NOT NULL sur une colonne existante (toutes les lignes doivent avoir une valeur)
-- CHANGE COLUMN : renommer ET modifier (deux noms requis)
ALTER TABLE Etudiant
CHANGE COLUMN villeEtu communeEtu VARCHAR(80) NOT NULL DEFAULT 'Casablanca';
-- Ancien nom : villeEtu | Nouveau nom : communeEtu | Nouveau type
| Commande | Renommer ? | Changer type ? | Usage |
|---|---|---|---|
ADD COLUMN | — (nouvelle) | — | Ajouter une nouvelle colonne |
MODIFY COLUMN | Non | Oui | Changer type/contraintes sans renommer |
CHANGE COLUMN | Oui | Oui | Renommer et/ou changer type |
DROP COLUMN | — | — | Supprimer une colonne (irréversible) |
RENAME COLUMN | Oui (MySQL 8+) | Non | Renommer uniquement (plus simple que CHANGE) |
-- DROP COLUMN : supprimer une colonne (irréversible !)
ALTER TABLE Etudiant
DROP COLUMN emailEtu;
-- Supprimer plusieurs colonnes
ALTER TABLE Formateur
DROP COLUMN telFormateur,
DROP COLUMN actif;
-- DROP INDEX : supprimer un index (sauf PK)
ALTER TABLE Etudiant
DROP INDEX emailEtu; -- Supprime l'index UNIQUE sur emailEtu
-- ou : DROP INDEX nom_index ON table;
-- DROP FOREIGN KEY : supprimer une contrainte FK
-- D'abord trouver le nom de la contrainte :
SHOW CREATE TABLE Session;
-- Puis supprimer :
ALTER TABLE Session
DROP FOREIGN KEY fk_Sess_Formateur;
-- Note : supprimer la FK ne supprime pas la colonne numFormateur
-- RENAME TABLE / RENAME TO
ALTER TABLE Session RENAME TO SessionFormation;
-- ou : RENAME TABLE Session TO SessionFormation;
-- DROP TABLE : supprimer une table et toutes ses données (irréversible)
DROP TABLE IF EXISTS SessionFormation;
-- IF EXISTS : pas d'erreur si la table n'existe pas
-- Supprimer plusieurs tables
DROP TABLE IF EXISTS Inscription, Session, Formation, Filiere;
-- Attention : respecter l'ordre inverse des FK (enfants avant parents)
-- DROP DATABASE : supprimer toute la base (EXTRÊMEMENT dangereux)
DROP DATABASE IF EXISTS skolabase_test;
Si une table A a une FK vers B, on ne peut pas supprimer B sans avoir supprimé A d'abord. Pour SkolaBase, l'ordre de suppression inverse l'ordre de création : Inscription → Session → Formation → Filiere & Formateur → Etudiant. Ou utiliser SET FOREIGN_KEY_CHECKS = 0 avant (à faire avec précaution en développement uniquement).
Un index accélère les recherches sur une colonne. Sans index, MySQL lit toutes les lignes (full table scan). Avec index, il utilise une structure B-Tree pour trouver les lignes en quelques accès. Créer des index sur les colonnes fréquemment utilisées dans WHERE, JOIN et ORDER BY. Inconvénient : ralentit les INSERT/UPDATE.
-- Créer un index simple (recherches fréquentes sur villeEtu)
CREATE INDEX idx_Etu_Ville ON Etudiant(villeEtu);
-- Créer un index unique (valeurs distinctes obligatoires)
CREATE UNIQUE INDEX idx_Etu_Email ON Etudiant(emailEtu);
-- Ajouter un index via ALTER TABLE
ALTER TABLE Formation
ADD INDEX idx_Form_Filiere (codeFiliere), -- Accélérer les JOIN avec Filiere
ADD INDEX idx_Form_Niveau (niveauForm); -- Filtrer par niveau fréquemment
-- Index composite (colonne 1 + colonne 2)
CREATE INDEX idx_Sess_FormDate ON Session(codeForm, dateDebutSess);
-- Utilisé quand WHERE codeForm = ? AND dateDebutSess > ?
-- Lister les index d'une table
SHOW INDEX FROM Session;
-- Lister les index avec leur utilisation (MySQL 8+)
SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE, SEQ_IN_INDEX
FROM information_schema.STATISTICS
WHERE TABLE_NAME = 'Session' AND TABLE_SCHEMA = 'skolabase';
lieuSess en salleFormation dans la table Session sans changer son type. Quelle commande est la plus adaptée sous MySQL 8 ?DROP TABLE Formation mais la table Session a une FK vers Formation. Que se passe-t-il ?INSERT INTO
Insérer des données dans les tables SkolaBase — syntaxe complète, insertion multi-lignes, colonnes optionnelles et valeurs par défaut, AUTO_INCREMENT, fonctions de date (CURDATE(), NOW()), et INSERT ... ON DUPLICATE KEY UPDATE.
- Syntaxe
INSERT INTO ... VALUESetINSERT INTO ... SET - Insérer plusieurs lignes en une seule commande
- Omettre les colonnes avec DEFAULT ou NULL
- Récupérer l'id généré par AUTO_INCREMENT avec
LAST_INSERT_ID() - Insérer des dates avec
CURDATE(),NOW()et les littéraux de date INSERT ... ON DUPLICATE KEY UPDATE— insérer ou mettre à jour
-- Syntaxe 1 : lister les colonnes (recommandée)
INSERT INTO Filiere (codeFiliere, libelleFiliere)
VALUES ('INF', 'Informatique');
-- Syntaxe 2 : sans liste de colonnes (ordre strict !)
-- RISQUÉ : si la structure change, la requête peut échouer silencieusement
INSERT INTO Filiere
VALUES ('GES', 'Gestion'), ('ELE', 'Électronique');
-- Syntaxe 3 : SET (style objet, lisible)
INSERT INTO Formateur SET
nomFormateur = 'Alaoui',
prenomFormateur = 'Karim',
specialiteForm = 'Développement Web';
-- Colonnes avec DEFAULT : omettre la colonne ou utiliser DEFAULT
INSERT INTO Etudiant (numCINEtu, nomEtu, prenomEtu, dateNaissEtu)
VALUES ('A1234567', 'Bennani', 'Sara', '2001-03-15');
-- villeEtu prend la valeur DEFAULT 'Casablanca'
-- telEtu prend NULL (accepte NULL)
-- Utiliser explicitement DEFAULT et NULL
INSERT INTO Etudiant
VALUES ('B7654321', 'Idrissi', 'Youssef', '1999-07-22', DEFAULT, NULL);
-- ^ville ^tel
Sans liste de colonnes, l'ordre doit correspondre exactement à la définition de la table. Si une colonne est ajoutàe plus tard via ALTER TABLE, toutes les requêtes INSERT sans liste de colonnes décalent et insèrent des valeurs dans les mauvaises colonnes. Toujours préférer la syntaxe avec liste de colonnes explicite.
USE skolabase;
-- 1. Filières (pas de FK, insérer en premier)
INSERT INTO Filiere (codeFiliere, libelleFiliere) VALUES
('INF', 'Informatique'),
('GES', 'Gestion et Commerce'),
('ELE', 'Électronique et Électrotechnique'),
('BTP', 'Bâtiment et Travaux Publics');
-- 2. Formations
INSERT INTO Formation (codeForm, titreForm, dureeForm, niveauForm, codeFiliere) VALUES
('DEV-WEB', 'Développement Web Full Stack', 120, 'Bac+2', 'INF'),
('DEV-MOB', 'Développement Mobile Android', 80, 'Bac+2', 'INF'),
('BDD-ADM', 'Administration des Bases de Données', 100, 'Bac+3', 'INF'),
('CPT-GEN', 'Comptabilité Générale', 90, 'Bac', 'GES'),
('MKT-DIG', 'Marketing Digital', 60, 'Bac+2', 'GES');
-- 3. Formateurs (numFormateur = AUTO_INCREMENT, ne pas le spécifier)
INSERT INTO Formateur (nomFormateur, prenomFormateur, specialiteForm) VALUES
('Alaoui', 'Karim', 'Développement Web, PHP, Laravel'),
('Bennani', 'Nadia', 'Bases de données, MySQL, Oracle'),
('Chraibi', 'Hassan', 'Réseaux, Administration système'),
('Idrissi', 'Leila', 'Marketing digital, SEO');
-- Récupérer le dernier ID généré
SELECT LAST_INSERT_ID();
-- Résultat : 4 (dernier numFormateur inséré)
-- 4. Sessions
INSERT INTO Session (codeSess, dateDebutSess, dateFinSess, lieuSess, numFormateur, codeForm) VALUES
('SESS-2024-01', '2024-09-02', '2024-12-20', 'Salle A101 - Casablanca', 1, 'DEV-WEB'),
('SESS-2024-02', '2024-09-09', '2024-11-29', 'Salle B205 - Rabat', 2, 'BDD-ADM'),
('SESS-2024-03', '2024-10-07', '2024-12-13', 'Salle A102 - Casablanca', 1, 'DEV-MOB'),
('SESS-2025-01', '2025-01-13', '2025-04-04', 'Salle A101 - Casablanca', 1, 'DEV-WEB');
-- 5. Étudiants (données marocaines)
INSERT INTO Etudiant (numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu) VALUES
('A1234567', 'Alaoui', 'Karim', '2002-05-15', 'Casablanca', '0612345678'),
('B7654321', 'Bennani', 'Sara', '2001-11-22', 'Rabat', '0698765432'),
('C2468135', 'Chraibi', 'Youssef', '2003-01-08', 'Marrakech', '0635791246'),
('D1357924', 'Idrissi', 'Fatima', '2002-08-30', 'Fès', '0674513892'),
('E9876543', 'Mansouri', 'Amine', '2001-03-17', 'Casablanca', '0656789012'),
('F3692581', 'Tazi', 'Zineb', '2003-06-25', 'Tanger', '0678901234');
-- 6. Inscriptions (FK vers Etudiant et Session)
INSERT INTO Inscription (numCINEtu, codeSess, noteInscription) VALUES
('A1234567', 'SESS-2024-01', 14.50),
('B7654321', 'SESS-2024-01', 16.00),
('C2468135', 'SESS-2024-01', 11.75),
('A1234567', 'SESS-2024-02', 13.25),
('D1357924', 'SESS-2024-02', 17.50),
('E9876543', 'SESS-2024-01', NULL), -- Note pas encore saisie
('F3692581', 'SESS-2024-03', 15.00);
-- Fonctions de date utiles dans INSERT
INSERT INTO Session (codeSess, dateDebutSess, dateFinSess, lieuSess, numFormateur, codeForm)
VALUES (
'SESS-2025-02',
CURDATE(), -- Date du jour
DATE_ADD(CURDATE(), INTERVAL 3 MONTH), -- Dans 3 mois
'Salle C301 - Casablanca',
2,
'CPT-GEN'
);
-- Littéraux de date (format ISO 8601 recommandé)
INSERT INTO Etudiant (numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu)
VALUES ('G1593570', 'Guessous', 'Imane', '2004-02-14', 'Agadir');
-- Format : 'AAAA-MM-JJ' pour DATE
-- Format : 'AAAA-MM-JJ HH:MM:SS' pour DATETIME
-- INSERT ... SELECT : copier des données depuis une autre table
INSERT INTO Formation (codeForm, titreForm, dureeForm, niveauForm, codeFiliere)
SELECT
CONCAT('CPY-', codeForm), -- Nouveau code préfixé
CONCAT('[Archivé] ', titreForm),
dureeForm,
niveauForm,
codeFiliere
FROM Formation
WHERE codeFiliere = 'INF'; -- Copier toutes les formations informatique
-- INSERT ... ON DUPLICATE KEY UPDATE : insérer ou mettre à jour
INSERT INTO Inscription (numCINEtu, codeSess, noteInscription)
VALUES ('A1234567', 'SESS-2024-01', 15.00)
ON DUPLICATE KEY UPDATE noteInscription = 15.00;
-- Si la PK (A1234567, SESS-2024-01) existe déjà : met à jour la note
-- Si elle n'existe pas : insère la nouvelle ligne
-- Compter les lignes affectées
-- INSERT normal : 1 (insertion)
-- ON DUPLICATE KEY : 2 (mise à jour) ou 1 (insertion)
SELECT ROW_COUNT();
numFormateur (AUTO_INCREMENT). Que retourne LAST_INSERT_ID() si 3 formateurs avaient déjà été insérés ?INSERT INTO Inscription VALUES ('Z9999999', 'SESS-2024-01', 12.00). L'étudiant Z9999999 n'existe pas dans la table Etudiant. Que se passe-t-il ?UPDATE, DELETE & TRUNCATE
Modifier les données avec UPDATE SET WHERE, supprimer avec DELETE FROM WHERE, différence entre DELETE et TRUNCATE, effets de ON DELETE CASCADE, et bonnes pratiques pour éviter les destructions accidentelles.
UPDATE table SET col = val WHERE condition— modifier des lignes ciblées- Mettre à jour plusieurs colonnes simultanément
- Mettre à jour avec des expressions et des sous-requêtes
DELETE FROM table WHERE condition— supprimer des lignes ciblées- Différence fondamentale
DELETEvsTRUNCATE - Sécuriser les UPDATE/DELETE — tester avec SELECT d'abord
Avant chaque UPDATE : exécuter d'abord SELECT * FROM table WHERE <même condition> pour vérifier quelles lignes seront affectées. Un UPDATE sans WHERE modifie toutes les lignes de la table — irréversible sans sauvegarde.
-- Vérifier d'abord ce qui sera modifié
SELECT * FROM Etudiant WHERE numCINEtu = 'A1234567';
-- UPDATE simple : corriger le téléphone d'un étudiant
UPDATE Etudiant
SET telEtu = '0612000001'
WHERE numCINEtu = 'A1234567';
-- UPDATE multi-colonnes : corriger plusieurs champs
UPDATE Etudiant
SET
villeEtu = 'Rabat',
telEtu = '0698001122'
WHERE numCINEtu = 'B7654321';
-- UPDATE avec expression : ajouter 1 point bonus à toutes les notes > 10
UPDATE Inscription
SET noteInscription = LEAST(noteInscription + 1, 20)
-- LEAST(x, 20) garantit que la note ne dépasse pas 20
WHERE noteInscription > 10 AND noteInscription IS NOT NULL;
-- UPDATE toutes les lignes (intentionnel : sans WHERE)
-- Désactiver tous les formateurs (mise en maintenance)
UPDATE Formateur SET actif = FALSE;
-- ROW_COUNT() donne le nombre de lignes modifiées
SELECT ROW_COUNT();
-- UPDATE avec JOIN : mettre à jour en se basant sur une autre table
UPDATE Inscription i
JOIN Session s ON i.codeSess = s.codeSess
SET i.noteInscription = 0
WHERE s.dateFinSess < CURDATE()
AND i.noteInscription IS NULL;
-- Mettre 0 pour les inscriptions sans note des sessions terminées
-- Utiliser une transaction pour annuler si erreur
START TRANSACTION;
UPDATE Inscription
SET noteInscription = 18.00
WHERE numCINEtu = 'D1357924' AND codeSess = 'SESS-2024-02';
-- Vérifier le résultat avant de valider
SELECT * FROM Inscription WHERE numCINEtu = 'D1357924';
-- Si correct : valider
COMMIT;
-- Si erreur : annuler
-- ROLLBACK;
-- Tester d'abord avec SELECT
SELECT * FROM Inscription WHERE codeSess = 'SESS-2024-03';
-- DELETE ciblé : supprimer les inscriptions d'une session
DELETE FROM Inscription
WHERE codeSess = 'SESS-2024-03';
-- DELETE avec LIMIT : supprimer au maximum N lignes (sécurité)
DELETE FROM Inscription
WHERE noteInscription IS NULL
LIMIT 10; -- Ne supprime pas plus de 10 lignes même si plus correspondent
-- DELETE avec condition sur date
DELETE FROM Session
WHERE dateFinSess < '2023-01-01'; -- Sessions terminées avant 2023
-- Vérifier l'effet ON DELETE CASCADE
-- Si ON DELETE CASCADE est défini sur Inscription → Etudiant :
DELETE FROM Etudiant WHERE numCINEtu = 'A1234567';
-- → Supprime automatiquement toutes les inscriptions de A1234567
-- DELETE avec JOIN : supprimer en se basant sur une autre table
DELETE i
FROM Inscription i
JOIN Session s ON i.codeSess = s.codeSess
WHERE s.codeForm = 'DEV-WEB'
AND s.dateFinSess < '2024-01-01';
-- Supprimer toutes les inscriptions aux sessions DEV-WEB terminées avant 2024
-- Confirmer le nombre de lignes supprimées
SELECT ROW_COUNT();
| Critère | DELETE | TRUNCATE |
|---|---|---|
| Type SQL | LMD (DML) — Manipulation | LDD (DDL) — Définition |
| Clause WHERE | Oui — suppression ciblée | Non — supprime TOUT |
| Transaction | Oui — peut être annulé avec ROLLBACK | Non — auto-commit, irréversible |
| AUTO_INCREMENT | Pas réinitialisé (continue depuis le max) | Réinitialisé à 1 |
| Triggers | Déclenche les triggers DELETE | Ne déclenche PAS les triggers |
| Clés étrangères | Respectées (CASCADE, RESTRICT) | Échoue si des FK pointent vers la table |
| Performance | Ligne par ligne — plus lent sur grandes tables | Recrée la table — très rapide |
| Usage | Supprimer des lignes spécifiques | Vider complètement une table de test |
-- DELETE tout (lent, annulable, garde AUTO_INCREMENT)
START TRANSACTION;
DELETE FROM Inscription; -- Supprime toutes les lignes ligne par ligne
ROLLBACK; -- Annulation possible !
-- La table Inscription retrouve toutes ses lignes
-- TRUNCATE (rapide, irréversible, remet AUTO_INCREMENT à 1)
TRUNCATE TABLE Formateur;
-- Équivalent à DROP TABLE + CREATE TABLE mais plus rapide
-- ROLLBACK impossible après TRUNCATE
-- Échoue si une autre table a une FK vers Formateur
-- Stratégie pour vider toutes les tables (développement)
SET FOREIGN_KEY_CHECKS = 0; -- Désactiver temporairement les FK
TRUNCATE TABLE Inscription;
TRUNCATE TABLE Session;
TRUNCATE TABLE Etudiant;
TRUNCATE TABLE Formation;
TRUNCATE TABLE Formateur;
TRUNCATE TABLE Filiere;
SET FOREIGN_KEY_CHECKS = 1; -- Réactiver les FK
-- ⚠ UNIQUEMENT en développement pour réinitialiser les données de test
UPDATE Etudiant SET villeEtu = 'Fès' sans clause WHERE. Que se passe-t-il ?DELETE FROM Formateur puis on en insère un nouveau. Quel sera son numFormateur ?SELECT — Fondamentaux
Maîtriser la requête SELECT de A à Z : colonnes et alias, DISTINCT, ordre d'évaluation MySQL, clause WHERE avec tous ses opérateurs (AND/OR/NOT/BETWEEN/IN/LIKE), ORDER BY multi-colonnes et LIMIT/OFFSET pour la pagination.
- Structure complète d'un SELECT et ordre d'évaluation MySQL
- Sélectionner des colonnes, utiliser des alias (
AS) DISTINCT— éliminer les doublons- Clause
WHERE:AND,OR,NOT,BETWEEN,IN,LIKE,IS NULL ORDER BYmulti-colonnes (ASC/DESC),LIMITetOFFSET
-- Structure complète (l'ordre d'écriture est OBLIGATOIRE)
SELECT [DISTINCT] colonnes | expressions | * -- 5. Quoi retourner
FROM table -- 1. Depuis quelle table
[WHERE condition] -- 2. Filtrer les lignes
[GROUP BY colonnes] -- 3. Regrouper
[HAVING condition_groupe] -- 4. Filtrer les groupes
[ORDER BY colonnes [ASC|DESC]] -- 6. Trier
[LIMIT n [OFFSET m]]; -- 7. Limiter
-- Ordre d'ÉVALUATION MySQL (différent de l'ordre d'écriture !)
-- FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
-- Important : on ne peut pas utiliser un alias défini dans SELECT dans WHERE
-- (WHERE est évalué avant SELECT)
USE skolabase;
-- Toutes les colonnes (déconseillé en production)
SELECT * FROM Etudiant;
-- Colonnes spécifiques
SELECT numCINEtu, nomEtu, prenomEtu FROM Etudiant;
-- Alias avec AS (renommer le résultat)
SELECT
numCINEtu AS cin,
nomEtu AS nom,
prenomEtu AS prenom,
villeEtu AS ville
FROM Etudiant;
-- Alias sans AS (syntaxe courte)
SELECT nomEtu nom, prenomEtu prenom FROM Etudiant;
-- Colonne calculée avec alias
SELECT
numCINEtu,
CONCAT(prenomEtu, ' ', nomEtu) AS nom_complet,
YEAR(CURDATE()) - YEAR(dateNaissEtu) AS age_approx
FROM Etudiant;
-- DISTINCT : éliminer les doublons
SELECT DISTINCT villeEtu FROM Etudiant;
-- Retourne chaque ville une seule fois
SELECT DISTINCT codeFiliere FROM Formation;
-- Toutes les filières utilisées
-- DISTINCT sur plusieurs colonnes (combinaison unique)
SELECT DISTINCT villeEtu, nomEtu FROM Etudiant;
-- Lignes uniques (ville + nom)
-- Comparaison simple
SELECT * FROM Etudiant WHERE villeEtu = 'Casablanca';
SELECT * FROM Inscription WHERE noteInscription >= 14;
SELECT * FROM Formation WHERE dureeForm != 120; -- != ou <>
-- AND / OR (AND a priorité sur OR)
SELECT * FROM Etudiant
WHERE villeEtu = 'Casablanca' AND nomEtu = 'Alaoui';
SELECT * FROM Etudiant
WHERE villeEtu = 'Casablanca' OR villeEtu = 'Rabat';
-- Parenthèses pour forcer la priorité
SELECT * FROM Etudiant
WHERE (villeEtu = 'Casablanca' OR villeEtu = 'Rabat')
AND nomEtu LIKE 'A%';
-- NOT : inverser une condition
SELECT * FROM Inscription WHERE NOT noteInscription >= 14;
-- Équivalent à : WHERE noteInscription < 14
-- BETWEEN : plage inclusive (inclut les bornes)
SELECT * FROM Inscription
WHERE noteInscription BETWEEN 10 AND 15;
-- Équivalent à : noteInscription >= 10 AND noteInscription <= 15
SELECT * FROM Session
WHERE dateDebutSess BETWEEN '2024-01-01' AND '2024-12-31';
-- IN : liste de valeurs
SELECT * FROM Etudiant
WHERE villeEtu IN ('Casablanca', 'Rabat', 'Marrakech');
-- Plus lisible que OR multiples
SELECT * FROM Formation
WHERE niveauForm IN ('Bac+2', 'Bac+3');
-- NOT IN : exclure une liste
SELECT * FROM Formation
WHERE codeFiliere NOT IN ('GES', 'ELE');
-- LIKE : recherche par motif
-- % = n'importe quelle suite de caractères (0 ou plus)
-- _ = exactement 1 caractère
SELECT * FROM Etudiant WHERE nomEtu LIKE 'A%'; -- Commence par A
SELECT * FROM Etudiant WHERE nomEtu LIKE '%ani'; -- Finit par ani
SELECT * FROM Etudiant WHERE nomEtu LIKE '%ala%'; -- Contient ala
SELECT * FROM Etudiant WHERE numCINEtu LIKE 'A______'; -- A + 6 chars
SELECT * FROM Formation WHERE titreForm LIKE '%Web%';
-- IS NULL / IS NOT NULL (JAMAIS = NULL)
SELECT * FROM Inscription WHERE noteInscription IS NULL;
SELECT * FROM Etudiant WHERE telEtu IS NOT NULL;
-- Erreur classique : WHERE noteInscription = NULL (ne retourne RIEN)
En SQL, NULL = NULL retourne UNKNOWN (pas TRUE). Toute comparaison avec NULL via =, !=, <, etc. retourne UNKNOWN et la ligne est exclue. Utiliser toujours IS NULL ou IS NOT NULL.
-- ORDER BY simple (ASC par défaut)
SELECT * FROM Etudiant ORDER BY nomEtu ASC;
SELECT * FROM Etudiant ORDER BY nomEtu; -- ASC implicite
-- ORDER BY décroissant
SELECT * FROM Inscription ORDER BY noteInscription DESC;
-- ORDER BY multi-colonnes (1ère colonne prioritaire)
SELECT * FROM Etudiant
ORDER BY villeEtu ASC, nomEtu ASC;
-- Trier par ville, puis par nom dans chaque ville
SELECT * FROM Inscription
ORDER BY noteInscription DESC, numCINEtu ASC;
-- ORDER BY sur alias (ORDER BY est évalué après SELECT)
SELECT nomEtu, prenomEtu,
CONCAT(prenomEtu, ' ', nomEtu) AS nom_complet
FROM Etudiant
ORDER BY nom_complet ASC; -- L'alias est utilisable dans ORDER BY
-- LIMIT : retourner seulement N lignes
SELECT * FROM Etudiant LIMIT 3; -- Les 3 premières lignes
SELECT * FROM Inscription
ORDER BY noteInscription DESC LIMIT 5; -- Top 5 notes
-- LIMIT + OFFSET : pagination
-- Page 1 : LIMIT 3 OFFSET 0 (lignes 1-3)
-- Page 2 : LIMIT 3 OFFSET 3 (lignes 4-6)
-- Page 3 : LIMIT 3 OFFSET 6 (lignes 7-9)
SELECT * FROM Etudiant
ORDER BY nomEtu
LIMIT 3 OFFSET 0; -- Page 1
SELECT * FROM Etudiant
ORDER BY nomEtu
LIMIT 3 OFFSET 3; -- Page 2
-- Syntaxe alternative : LIMIT offset, count
SELECT * FROM Etudiant ORDER BY nomEtu LIMIT 3, 3; -- Offset 3, count 3
-- ORDER BY personnalisé avec FIELD()
SELECT * FROM Formation
ORDER BY FIELD(niveauForm, 'Technicien', 'Bac', 'Bac+2', 'Bac+3');
-- Trie dans l'ordre spécifié (pas alphabétique)
SELECT * FROM Inscription WHERE noteInscription = NULL — combien de lignes retourne cette requête si 2 inscriptions ont une note NULL ?GROUP BY & HAVING
Regrouper les données avec GROUP BY, ordre d'exécution MySQL, différence fondamentale HAVING vs WHERE, combinaison complète SELECT/FROM/WHERE/GROUP BY/HAVING/ORDER BY appliquée aux statistiques SkolaBase.
- Comprendre le regroupement :
GROUP BYcrée des groupes de lignes - Ordre d'évaluation : FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
HAVINGfiltre les groupes (après GROUP BY) vsWHEREfiltre les lignes (avant)- Combiner WHERE + GROUP BY + HAVING dans une même requête
GROUP BYavecROLLUPpour les sous-totaux
Dans un SELECT avec GROUP BY, chaque colonne du SELECT doit soit faire partie du GROUP BY, soit être encapsulée dans une fonction d'agrégation (COUNT, SUM, AVG, MAX, MIN). Sinon MySQL retourne une erreur (en mode strict) ou une valeur arbitraire.
-- Nombre d'étudiants par ville
SELECT villeEtu, COUNT(*) AS nb_etudiants
FROM Etudiant
GROUP BY villeEtu;
-- Résultat :
-- villeEtu | nb_etudiants
-- Casablanca | 2
-- Rabat | 1
-- Marrakech | 1
-- Moyenne des notes par session
SELECT
codeSess,
COUNT(*) AS nb_inscrits,
COUNT(noteInscription) AS nb_notes_saisies,
AVG(noteInscription) AS moyenne,
MIN(noteInscription) AS note_min,
MAX(noteInscription) AS note_max
FROM Inscription
GROUP BY codeSess;
-- Nombre de formations par filière
SELECT
f.libelleFiliere,
COUNT(fm.codeForm) AS nb_formations,
SUM(fm.dureeForm) AS heures_totales
FROM Filiere f
LEFT JOIN Formation fm ON f.codeFiliere = fm.codeFiliere
GROUP BY f.codeFiliere, f.libelleFiliere
ORDER BY nb_formations DESC;
-- GROUP BY avec expression (regrouper par année de début)
SELECT
YEAR(dateDebutSess) AS annee,
COUNT(*) AS nb_sessions
FROM Session
GROUP BY YEAR(dateDebutSess)
ORDER BY annee;
| Critère | WHERE | HAVING |
|---|---|---|
| Étape d'évaluation | Avant GROUP BY (sur les lignes) | Après GROUP BY (sur les groupes) |
| Peut utiliser | Colonnes brutes de la table | Colonnes brutes ET fonctions d'agrégation |
| Peut utiliser les alias SELECT | Non (SELECT pas encore évalué) | Oui dans MySQL (extension) |
| Exemple SkolaBase | WHERE villeEtu = 'Casablanca' | HAVING COUNT(*) > 2 |
-- Sessions avec plus de 2 inscrits
SELECT codeSess, COUNT(*) AS nb_inscrits
FROM Inscription
GROUP BY codeSess
HAVING COUNT(*) > 2;
-- Étudiants inscrits à plus d'une session
SELECT numCINEtu, COUNT(*) AS nb_sessions
FROM Inscription
GROUP BY numCINEtu
HAVING COUNT(*) > 1;
-- Combinaison WHERE + GROUP BY + HAVING
-- Sessions de 2024 avec moyenne > 13
SELECT
codeSess,
AVG(noteInscription) AS moyenne
FROM Inscription
WHERE noteInscription IS NOT NULL -- WHERE : exclure les notes NULL d'abord
GROUP BY codeSess -- GROUP BY : regrouper par session
HAVING AVG(noteInscription) > 13 -- HAVING : garder seulement moyenne > 13
ORDER BY moyenne DESC;
-- WRONG : ne pas mettre une fonction d'agrégation dans WHERE
-- SELECT codeSess FROM Inscription WHERE AVG(noteInscription) > 13; -- ERREUR
-- La bonne version : utiliser HAVING
-- Filières avec plus de 2 formations de niveau Bac+2
SELECT
codeFiliere,
COUNT(*) AS nb_formations_bac2
FROM Formation
WHERE niveauForm = 'Bac+2' -- WHERE filtre les lignes individuelles
GROUP BY codeFiliere
HAVING COUNT(*) >= 2; -- HAVING filtre les groupes résultants
-- WITH ROLLUP : sous-totaux et total général
SELECT
COALESCE(villeEtu, 'TOTAL') AS ville,
COUNT(*) AS nb_etudiants
FROM Etudiant
GROUP BY villeEtu WITH ROLLUP;
-- Ajoute une ligne TOTAL à la fin
SELECT codeSess FROM Inscription WHERE AVG(noteInscription) > 13 GROUP BY codeSess retourne-t-il une erreur ?Fonctions SQL
Fonctions mathématiques (ABS, CEIL, FLOOR, ROUND, MOD), fonctions chaînes (CONCAT, LEFT, RIGHT, SUBSTRING, UPPER, LOWER, TRIM, REPLACE, LENGTH), fonctions de dates (CURDATE, NOW, DATEDIFF, DATE_FORMAT, DATE_ADD, TIMESTAMPDIFF) — toutes appliquées à SkolaBase.
- Fonctions mathématiques :
ABS,CEIL,FLOOR,ROUND,TRUNCATE,MOD,POWER - Fonctions chaînes :
CONCAT,LENGTH,UPPER/LOWER,TRIM,LEFT/RIGHT,SUBSTRING,REPLACE,LPAD/RPAD - Fonctions de dates :
CURDATE,NOW,YEAR/MONTH/DAY,DATEDIFF,DATE_ADD,DATE_FORMAT,TIMESTAMPDIFF
-- ABS : valeur absolue
SELECT ABS(-14.5); -- 14.5
SELECT ABS(noteInscription - 10) AS ecart_au_seuil
FROM Inscription;
-- CEIL / CEILING : arrondir vers le haut (entier supérieur)
SELECT CEIL(13.1); -- 14
SELECT CEIL(13.0); -- 13
-- FLOOR : arrondir vers le bas (entier inférieur)
SELECT FLOOR(13.9); -- 13
-- ROUND : arrondir au plus proche (n décimales)
SELECT ROUND(13.456, 2); -- 13.46
SELECT ROUND(13.454, 2); -- 13.45
SELECT ROUND(13.5); -- 14 (0 décimales)
-- TRUNCATE : tronquer sans arrondir
SELECT TRUNCATE(13.999, 2); -- 13.99 (pas d'arrondi)
SELECT TRUNCATE(13.999, 0); -- 13
-- MOD : modulo (reste de la division)
SELECT MOD(17, 5); -- 2
SELECT MOD(numFormateur, 2) AS parite FROM Formateur;
-- 0 = pair, 1 = impair
-- POWER / POW : puissance
SELECT POWER(2, 10); -- 1024
-- SQRT : racine carrée
SELECT SQRT(144); -- 12
-- Exemple concret : arrondir les moyennes à 2 décimales
SELECT
codeSess,
ROUND(AVG(noteInscription), 2) AS moyenne_arrondie
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY codeSess;
-- CONCAT : concaténer des chaînes
SELECT CONCAT(prenomEtu, ' ', nomEtu) AS nom_complet FROM Etudiant;
SELECT CONCAT('ETU-', numCINEtu) AS code_etudiant FROM Etudiant;
-- CONCAT_WS : concaténer avec séparateur (WS = With Separator)
SELECT CONCAT_WS(' - ', nomEtu, prenomEtu, villeEtu) AS fiche FROM Etudiant;
-- Alaoui - Karim - Casablanca
-- LENGTH : longueur en octets (CHAR_LENGTH pour les caractères)
SELECT nomEtu, LENGTH(nomEtu) AS longueur FROM Etudiant;
SELECT nomEtu, CHAR_LENGTH(nomEtu) AS nb_chars FROM Etudiant;
-- LENGTH != CHAR_LENGTH pour les caractères UTF-8 multi-octets (ç, é, ب...)
-- UPPER / LOWER : majuscules / minuscules
SELECT UPPER(nomEtu) AS nom_maj FROM Etudiant; -- ALAOUI
SELECT LOWER(nomEtu) AS nom_min FROM Etudiant; -- alaoui
-- TRIM / LTRIM / RTRIM : supprimer les espaces
SELECT TRIM(' Karim '); -- 'Karim'
SELECT LTRIM(' Karim'); -- 'Karim' (espaces gauche)
SELECT RTRIM('Karim '); -- 'Karim' (espaces droite)
-- LEFT / RIGHT : extraire N caractères à gauche/droite
SELECT LEFT(numCINEtu, 1) AS lettre_cin FROM Etudiant; -- 'A', 'B', 'C'...
SELECT RIGHT(numCINEtu, 7) AS chiffres_cin FROM Etudiant; -- '1234567'
-- SUBSTRING / SUBSTR : extraire une sous-chaîne
SELECT SUBSTRING(numCINEtu, 2, 7) AS chiffres FROM Etudiant;
-- Depuis position 2, 7 caractères
SELECT SUBSTRING(codeSess, 6, 4) AS annee_sess FROM Session;
-- 'SESS-2024-01' → '2024'
-- REPLACE : remplacer une sous-chaîne
SELECT REPLACE(titreForm, 'Développement', 'Dev') FROM Formation;
SELECT REPLACE(nomEtu, 'El ', '') AS nom_court FROM Etudiant;
-- LPAD / RPAD : compléter avec un caractère
SELECT LPAD(numFormateur, 4, '0') AS code_form FROM Formateur;
-- 1 → '0001', 12 → '0012'
-- LOCATE / POSITION : trouver la position d'une sous-chaîne
SELECT LOCATE('Web', titreForm) AS pos_web FROM Formation;
-- 0 = non trouvé, > 0 = position
-- INSTR : similaire à LOCATE
SELECT titreForm FROM Formation WHERE INSTR(titreForm, 'Web') > 0;
-- Date et heure actuelles
SELECT CURDATE(); -- '2024-11-15' (date seule)
SELECT CURTIME(); -- '14:23:05' (heure seule)
SELECT NOW(); -- '2024-11-15 14:23:05'
SELECT SYSDATE(); -- Similaire à NOW()
-- Extraire des parties de date
SELECT
dateNaissEtu,
YEAR(dateNaissEtu) AS annee,
MONTH(dateNaissEtu) AS mois,
DAY(dateNaissEtu) AS jour,
DAYNAME(dateNaissEtu) AS jour_semaine, -- 'Monday', 'Tuesday'...
MONTHNAME(dateNaissEtu) AS nom_mois -- 'January', 'February'...
FROM Etudiant;
-- DATEDIFF : différence en jours
SELECT
codeSess,
dateDebutSess,
dateFinSess,
DATEDIFF(dateFinSess, dateDebutSess) AS duree_jours
FROM Session;
-- Âge en années avec TIMESTAMPDIFF
SELECT
numCINEtu,
CONCAT(prenomEtu, ' ', nomEtu) AS nom_complet,
TIMESTAMPDIFF(YEAR, dateNaissEtu, CURDATE()) AS age
FROM Etudiant
ORDER BY age;
-- DATE_ADD / DATE_SUB : ajouter/soustraire un intervalle
SELECT DATE_ADD(CURDATE(), INTERVAL 30 DAY) AS dans_30_jours;
SELECT DATE_ADD(CURDATE(), INTERVAL 3 MONTH) AS dans_3_mois;
SELECT DATE_SUB(CURDATE(), INTERVAL 1 YEAR) AS il_y_a_1_an;
-- Intervalles disponibles : SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR
-- DATE_FORMAT : formater une date
SELECT DATE_FORMAT(dateDebutSess, '%d/%m/%Y') AS date_fr
FROM Session;
-- '02/09/2024'
SELECT DATE_FORMAT(dateDebutSess, '%W %d %M %Y') AS date_longue
FROM Session;
-- 'Monday 02 September 2024'
SELECT DATE_FORMAT(dateDebutSess, '%Y-%m') AS annee_mois
FROM Session
GROUP BY DATE_FORMAT(dateDebutSess, '%Y-%m');
-- Grouper par mois : '2024-09', '2024-10'...
-- WEEK / WEEKOFYEAR : numéro de semaine
SELECT codeSess, WEEK(dateDebutSess) AS semaine FROM Session;
-- Sessions dont la date de fin est dépassée
SELECT codeSess, dateFinSess
FROM Session
WHERE dateFinSess < CURDATE();
-- Sessions en cours aujourd'hui
SELECT codeSess, dateDebutSess, dateFinSess
FROM Session
WHERE CURDATE() BETWEEN dateDebutSess AND dateFinSess;
| Format | Signification | Exemple |
|---|---|---|
%Y | Année 4 chiffres | 2024 |
%y | Année 2 chiffres | 24 |
%m | Mois 2 chiffres (01–12) | 09 |
%M | Nom du mois | September |
%d | Jour 2 chiffres (01–31) | 02 |
%D | Jour avec suffixe | 2nd |
%W | Nom du jour | Monday |
%H | Heure (00–23) | 14 |
%i | Minutes (00–59) | 23 |
%s | Secondes (00–59) | 05 |
SELECT LENGTH('Casablanca') vs SELECT CHAR_LENGTH('Casablanca') — même résultat ? Et pour 'Fès' ?Fonctions d'agrégation
Maîtriser les 5 fonctions d'agrégation — COUNT(*) vs COUNT(col), SUM, AVG, MIN, MAX — comportement avec NULL, COUNT(DISTINCT), et application aux statistiques complètes de SkolaBase.
COUNT(*)vsCOUNT(colonne)— différence avec les valeurs NULLSUM,AVG— comportement avec NULL (ignoré automatiquement)MIN,MAX— sur numériques, chaînes et datesCOUNT(DISTINCT col)— compter les valeurs uniques- Combiner plusieurs fonctions d'agrégation dans un même SELECT
COUNT(*) compte toutes les lignes du groupe, y compris celles avec des NULL. COUNT(colonne) compte uniquement les lignes où la colonne n'est pas NULL. Exemple : si 7 inscriptions dont 1 avec noteInscription = NULL, alors COUNT(*) = 7 et COUNT(noteInscription) = 6.
-- COUNT(*) : toutes les lignes (y compris NULL)
SELECT COUNT(*) AS total_inscriptions FROM Inscription; -- 7
-- COUNT(colonne) : ignorer les NULL
SELECT COUNT(noteInscription) AS notes_saisies FROM Inscription; -- 6
-- Voir la différence
SELECT
COUNT(*) AS total_inscrits,
COUNT(noteInscription) AS notes_saisies,
COUNT(*) - COUNT(noteInscription) AS notes_manquantes
FROM Inscription;
-- COUNT(DISTINCT) : compter les valeurs uniques
SELECT COUNT(DISTINCT numCINEtu) AS nb_etudiants_inscrits
FROM Inscription;
-- Nombre d'étudiants différents qui ont au moins une inscription
SELECT COUNT(DISTINCT villeEtu) AS nb_villes FROM Etudiant;
-- Statistiques complètes par session
SELECT
codeSess,
COUNT(*) AS nb_inscrits,
COUNT(noteInscription) AS notes_saisies,
COUNT(*) - COUNT(noteInscription) AS notes_manquantes,
ROUND(AVG(noteInscription), 2) AS moyenne,
MIN(noteInscription) AS note_min,
MAX(noteInscription) AS note_max,
SUM(noteInscription) AS total_notes
FROM Inscription
GROUP BY codeSess
ORDER BY codeSess;
-- Toutes les fonctions ignorent automatiquement les NULL
SELECT
SUM(noteInscription) AS somme, -- Somme des notes non-NULL
AVG(noteInscription) AS moyenne, -- Moyenne des notes non-NULL
MIN(noteInscription) AS note_min,
MAX(noteInscription) AS note_max
FROM Inscription;
-- MIN / MAX sur des chaînes (ordre alphabétique)
SELECT MIN(nomEtu) AS premier_alpha, MAX(nomEtu) AS dernier_alpha
FROM Etudiant;
-- MIN / MAX sur des dates
SELECT
MIN(dateDebutSess) AS premiere_session,
MAX(dateFinSess) AS derniere_fin
FROM Session;
-- Total des heures de formation par filière
SELECT
fi.libelleFiliere,
SUM(fo.dureeForm) AS total_heures,
AVG(fo.dureeForm) AS duree_moyenne,
COUNT(fo.codeForm) AS nb_formations
FROM Filiere fi
JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
GROUP BY fi.codeFiliere, fi.libelleFiliere
ORDER BY total_heures DESC;
-- Taux de réussite par session (note >= 10)
SELECT
codeSess,
COUNT(*) AS total,
SUM(CASE WHEN noteInscription >= 10 THEN 1 ELSE 0 END) AS admis,
ROUND(100 * SUM(CASE WHEN noteInscription >= 10 THEN 1 ELSE 0 END)
/ COUNT(noteInscription), 1) AS taux_reussite_pct
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY codeSess;
noteInscription = NULL. Quel est le résultat de AVG(noteInscription) ?Expressions & Opérateurs avancés
Opérateurs arithmétiques, logiques et de comparaison, expressions conditionnelles CASE WHEN et IF(), gestion des NULL avec COALESCE et NULLIF, et précédence des opérateurs.
- Opérateurs arithmétiques :
+,-,*,/,DIV,MOD - Précédence des opérateurs et rôle des parenthèses
CASE WHEN ... THEN ... ELSE ... END— expression conditionnelle puissanteIF(condition, valeur_vrai, valeur_faux)— ternaire SQLCOALESCE(a, b, c)— première valeur non-NULLNULLIF(a, b)— retourner NULL si a = b
-- Opérateurs de base
SELECT 10 + 3; -- 13 addition
SELECT 10 - 3; -- 7 soustraction
SELECT 10 * 3; -- 30 multiplication
SELECT 10 / 3; -- 3.33 division réelle (DECIMAL)
SELECT 10 DIV 3; -- 3 division entière
SELECT 10 MOD 3; -- 1 modulo (reste)
SELECT 10 % 3; -- 1 alias de MOD
-- Priorité : * / avant + - (comme en mathématiques)
SELECT 2 + 3 * 4; -- 14 (pas 20)
SELECT (2 + 3) * 4; -- 20
-- Applications SkolaBase
SELECT
codeSess,
dureeForm,
dureeForm * 60 AS duree_minutes,
dureeForm / 8 AS duree_jours,
ROUND(dureeForm / 8) AS duree_jours_arrondis
FROM Session
JOIN Formation USING (codeForm);
-- Bonus de 5% sur les notes < 14, plafonné à 20
SELECT
numCINEtu,
noteInscription AS note_initiale,
LEAST(noteInscription * 1.05, 20) AS note_avec_bonus
FROM Inscription
WHERE noteInscription < 14 AND noteInscription IS NOT NULL;
-- CASE recherché (condition booléenne — le plus flexible)
SELECT
numCINEtu,
codeSess,
noteInscription,
CASE
WHEN noteInscription IS NULL THEN 'Non évalué'
WHEN noteInscription >= 18 THEN 'Excellent'
WHEN noteInscription >= 14 THEN 'Bien'
WHEN noteInscription >= 12 THEN 'Assez bien'
WHEN noteInscription >= 10 THEN 'Passable'
ELSE 'Insuffisant'
END AS mention
FROM Inscription;
-- CASE simple (comparer une colonne à des valeurs)
SELECT
codeFiliere,
CASE codeFiliere
WHEN 'INF' THEN 'Informatique'
WHEN 'GES' THEN 'Gestion'
WHEN 'ELE' THEN 'Électronique'
ELSE 'Autre filière'
END AS libelle_court
FROM Formation;
-- CASE dans GROUP BY / HAVING (classifier puis compter)
SELECT
CASE
WHEN noteInscription >= 10 THEN 'Admis'
ELSE 'Refusé'
END AS statut,
COUNT(*) AS nb
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY statut;
-- IF() : version courte pour 2 cas seulement
SELECT
numCINEtu,
noteInscription,
IF(noteInscription >= 10, 'Admis', 'Refusé') AS decision
FROM Inscription;
-- IF() dans SUM pour compter conditionnellement
SELECT
SUM(IF(noteInscription >= 10, 1, 0)) AS nb_admis,
SUM(IF(noteInscription < 10, 1, 0)) AS nb_refusés,
COUNT(noteInscription) AS total_notes
FROM Inscription;
-- COALESCE(a, b, c) : retourne la première valeur non-NULL
SELECT
numCINEtu,
COALESCE(telEtu, 'Téléphone non renseigné') AS contact
FROM Etudiant;
-- Si telEtu = NULL → 'Téléphone non renseigné'
-- Si telEtu = '0612345678' → '0612345678'
-- COALESCE avec plusieurs alternatives
SELECT
COALESCE(telEtu, emailEtu, numCINEtu) AS contact_prioritaire
FROM Etudiant;
-- Essaie tel, puis email, puis CIN (toujours non-NULL)
-- Afficher les notes avec valeur par défaut
SELECT
numCINEtu,
codeSess,
COALESCE(noteInscription, 0) AS note_affichee -- 0 si NULL
FROM Inscription;
-- IFNULL(col, valeur_si_null) : version MySQL de COALESCE à 2 args
SELECT
numCINEtu,
IFNULL(noteInscription, 'À évaluer') AS note
FROM Inscription;
-- NULLIF(a, b) : retourne NULL si a = b, sinon retourne a
-- Utilité : éviter la division par zéro
SELECT
codeSess,
SUM(noteInscription) / NULLIF(COUNT(noteInscription), 0) AS moyenne_securisee
FROM Inscription
GROUP BY codeSess;
-- Si COUNT = 0 (aucune note) → NULLIF retourne NULL → division évitée
-- Exemple : transformer 0 en NULL (ignorer les 0 dans une moyenne)
SELECT AVG(NULLIF(noteInscription, 0)) AS moyenne_sans_zeros
FROM Inscription;
IF(noteInscription >= 10, 'Admis', 'Refusé') et CASE WHEN noteInscription >= 10 THEN 'Admis' ELSE 'Refusé' END pour les notes NULL ?COALESCE(telEtu, emailEtu, 'Inconnu') — si telEtu = NULL et emailEtu = 'karim@mail.com', que retourne cette expression ?Sous-requêtes I — mono-ligne & IN
Définition et positionnement des sous-requêtes (SELECT, FROM, WHERE, HAVING), sous-requêtes mono-ligne (=, >, <), sous-requêtes multi-lignes avec IN et NOT IN, et sous-requêtes scalaires dans le SELECT.
- Définir une sous-requête et ses 4 positions possibles (SELECT, FROM, WHERE, HAVING)
- Sous-requête mono-ligne : retourne une seule valeur, utilisée avec =, >, <, >=, <=
- Sous-requête multi-lignes avec
INetNOT IN - Sous-requête scalaire dans le SELECT (colonne calculée)
- Sous-requête dans le FROM (table dérivée)
Une sous-requête (ou requête imbriquée) est un SELECT placé à l'intérieur d'une autre requête SQL. Elle est toujours entre parenthèses. La sous-requête est exécutée en premier et son résultat est utilisé par la requête externe. Elle permet de répondre à des questions en deux étapes : d'abord trouver une valeur ou un ensemble de valeurs, puis utiliser ce résultat comme filtre.
-- Position 1 : dans le SELECT (sous-requête scalaire)
SELECT
nomEtu,
(SELECT COUNT(*) FROM Inscription WHERE numCINEtu = e.numCINEtu) AS nb_inscriptions
FROM Etudiant e;
-- Position 2 : dans le FROM (table dérivée — doit avoir un alias)
SELECT *
FROM (
SELECT codeSess, AVG(noteInscription) AS moyenne
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY codeSess
) AS stats_sessions
WHERE moyenne > 13;
-- Position 3 : dans le WHERE (la plus courante)
SELECT * FROM Etudiant
WHERE numCINEtu IN (
SELECT numCINEtu FROM Inscription WHERE noteInscription >= 14
);
-- Position 4 : dans le HAVING
SELECT codeSess, AVG(noteInscription) AS moy
FROM Inscription
GROUP BY codeSess
HAVING AVG(noteInscription) > (
SELECT AVG(noteInscription) FROM Inscription
);
Si la sous-requête utilisée avec = retourne plusieurs lignes, MySQL retourne une erreur : Subquery returns more than 1 row. Si elle retourne zéro ligne, la comparaison retourne NULL (et la ligne est exclue). Vérifier que la sous-requête retourne toujours exactement une valeur.
-- Étudiants dont la note > moyenne générale
SELECT numCINEtu, noteInscription
FROM Inscription
WHERE noteInscription > (
SELECT AVG(noteInscription)
FROM Inscription
WHERE noteInscription IS NOT NULL
);
-- Session avec la durée de formation la plus longue
SELECT codeSess, dateDebutSess
FROM Session
WHERE codeForm = (
SELECT codeForm
FROM Formation
WHERE dureeForm = (SELECT MAX(dureeForm) FROM Formation)
);
-- Étudiant avec la meilleure note (unique)
SELECT e.numCINEtu, e.nomEtu, e.prenomEtu, i.noteInscription
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu
WHERE i.noteInscription = (
SELECT MAX(noteInscription) FROM Inscription
);
-- Formateur qui anime la session la plus récente
SELECT nomFormateur, prenomFormateur
FROM Formateur
WHERE numFormateur = (
SELECT numFormateur
FROM Session
ORDER BY dateDebutSess DESC
LIMIT 1
);
-- IN : étudiants qui ont au moins une inscription
SELECT numCINEtu, nomEtu, prenomEtu
FROM Etudiant
WHERE numCINEtu IN (
SELECT DISTINCT numCINEtu FROM Inscription
);
-- NOT IN : étudiants sans aucune inscription
SELECT numCINEtu, nomEtu, prenomEtu
FROM Etudiant
WHERE numCINEtu NOT IN (
SELECT DISTINCT numCINEtu FROM Inscription
);
-- Formations qui ont au moins une session
SELECT codeForm, titreForm
FROM Formation
WHERE codeForm IN (
SELECT DISTINCT codeForm FROM Session
);
-- Sessions avec note moyenne > 13
SELECT codeSess, dateDebutSess
FROM Session
WHERE codeSess IN (
SELECT codeSess
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY codeSess
HAVING AVG(noteInscription) > 13
);
-- Table dérivée : les 3 meilleures sessions par moyenne
SELECT s.codeSess, s.dateDebutSess, stats.moyenne
FROM Session s
JOIN (
SELECT codeSess, ROUND(AVG(noteInscription), 2) AS moyenne
FROM Inscription
WHERE noteInscription IS NOT NULL
GROUP BY codeSess
) AS stats ON s.codeSess = stats.codeSess
ORDER BY stats.moyenne DESC
LIMIT 3;
Si la sous-requête de NOT IN contient au moins un NULL, le résultat entier est vide (0 lignes) ! En effet, x NOT IN (1, 2, NULL) équivaut à x != 1 AND x != 2 AND x != NULL — et x != NULL retourne toujours UNKNOWN. Solution : ajouter WHERE colonne IS NOT NULL dans la sous-requête.
WHERE numCINEtu = (SELECT numCINEtu FROM Inscription WHERE noteInscription > 15) retourne une erreur. Pourquoi ?WHERE numCINEtu NOT IN (SELECT numCINEtu FROM Inscription WHERE noteInscription IS NULL) — si la sous-requête retourne (NULL, 'A123', 'B456'), combien de lignes retourne la requête externe ?Sous-requêtes II — ALL, ANY, EXISTS
ALL (comparaison avec toutes les valeurs), ANY/SOME (au moins une valeur), EXISTS et NOT EXISTS (test d'existence), sous-requêtes corrélées (référencent la requête externe), et comparaison avec les jointures.
ALL— vraie si la condition est vraie pour toutes les valeursANY/SOME— vraie si la condition est vraie pour au moins une valeurEXISTS— vrai si la sous-requête retourne au moins une ligneNOT EXISTS— vrai si la sous-requête retourne zéro ligne- Sous-requêtes corrélées — exécutées une fois par ligne de la requête externe
-- ALL : vrai si la comparaison est vraie pour TOUTES les valeurs
-- Étudiants dont la note est > TOUTES les notes de la session SESS-2024-02
SELECT numCINEtu, noteInscription
FROM Inscription
WHERE codeSess = 'SESS-2024-01'
AND noteInscription > ALL (
SELECT noteInscription
FROM Inscription
WHERE codeSess = 'SESS-2024-02'
AND noteInscription IS NOT NULL
);
-- Revient à : note > MAX(notes de SESS-2024-02)
-- Formations dont la durée est > TOUTES les formations de filière GES
SELECT codeForm, titreForm, dureeForm
FROM Formation
WHERE dureeForm > ALL (
SELECT dureeForm FROM Formation WHERE codeFiliere = 'GES'
);
-- Équivalent à : dureeForm > MAX(durée des formations GES)
-- ANY : vrai si la comparaison est vraie pour AU MOINS UNE valeur
-- Étudiants dont la note est > n'importe quelle note de SESS-2024-02
SELECT numCINEtu, noteInscription
FROM Inscription
WHERE codeSess = 'SESS-2024-01'
AND noteInscription > ANY (
SELECT noteInscription
FROM Inscription
WHERE codeSess = 'SESS-2024-02'
AND noteInscription IS NOT NULL
);
-- Revient à : note > MIN(notes de SESS-2024-02)
| Opérateur | Signification | Équivalent agrégat |
|---|---|---|
= ANY | Égal à au moins une valeur | Équivalent à IN |
<> ALL | Différent de toutes les valeurs | Équivalent à NOT IN |
> ALL | Supérieur à la valeur maximale | Équivalent à > MAX(...) |
> ANY | Supérieur à la valeur minimale | Équivalent à > MIN(...) |
< ALL | Inférieur à la valeur minimale | Équivalent à < MIN(...) |
< ANY | Inférieur à la valeur maximale | Équivalent à < MAX(...) |
EXISTS teste si une sous-requête retourne au moins une ligne. MySQL s'arrête dès la première ligne trouvée — très efficace. La convention SELECT 1 ou SELECT * dans la sous-requête EXISTS est habituelle (la valeur retournée n'importe pas, seule l'existence compte). Avantage sur IN : pas de problème avec les NULL.
-- EXISTS : étudiants qui ont AU MOINS UNE inscription
SELECT numCINEtu, nomEtu, prenomEtu
FROM Etudiant e
WHERE EXISTS (
SELECT 1
FROM Inscription i
WHERE i.numCINEtu = e.numCINEtu -- référence à la requête externe (corrélée)
);
-- MySQL arrête dès la 1ère inscription trouvée → très rapide
-- NOT EXISTS : étudiants SANS aucune inscription
SELECT numCINEtu, nomEtu, prenomEtu
FROM Etudiant e
WHERE NOT EXISTS (
SELECT 1
FROM Inscription i
WHERE i.numCINEtu = e.numCINEtu
);
-- Avantage sur NOT IN : pas de piège NULL
-- Formateurs qui n'ont animé AUCUNE session
SELECT numFormateur, nomFormateur
FROM Formateur f
WHERE NOT EXISTS (
SELECT 1 FROM Session s
WHERE s.numFormateur = f.numFormateur
);
-- Sessions qui n'ont aucun inscrit
SELECT codeSess, dateDebutSess
FROM Session s
WHERE NOT EXISTS (
SELECT 1 FROM Inscription i
WHERE i.codeSess = s.codeSess
);
-- Étudiants inscrits à TOUTES les sessions de 2024 (division relationnelle)
-- Formulation : étudiants pour lesquels il n'existe PAS de session 2024
-- à laquelle ils ne sont PAS inscrits
SELECT numCINEtu, nomEtu
FROM Etudiant e
WHERE NOT EXISTS (
SELECT codeSess
FROM Session
WHERE YEAR(dateDebutSess) = 2024
AND codeSess NOT IN (
SELECT codeSess FROM Inscription
WHERE numCINEtu = e.numCINEtu
)
);
Une sous-requête corrélée référence une colonne de la requête externe (via son alias). Elle est réexécutée pour chaque ligne de la requête externe. Plus lente qu'une sous-requête non-corrélée (qui s'exécute une seule fois), mais parfois inévitable. Sur les grandes tables, préférer une jointure à une sous-requête corrélée.
-- Pour chaque étudiant, compter ses inscriptions (sous-requête corrélée)
SELECT
e.numCINEtu,
e.nomEtu,
(SELECT COUNT(*)
FROM Inscription i
WHERE i.numCINEtu = e.numCINEtu) AS nb_inscriptions
FROM Etudiant e;
-- La sous-requête est exécutée une fois par étudiant
-- Pour chaque étudiant, afficher sa meilleure note
SELECT
e.numCINEtu,
e.nomEtu,
(SELECT MAX(noteInscription)
FROM Inscription i
WHERE i.numCINEtu = e.numCINEtu) AS meilleure_note
FROM Etudiant e;
-- Étudiants dont la note dans SESS-2024-01 est supérieure à leur propre moyenne
SELECT numCINEtu, noteInscription AS note_sess01
FROM Inscription i1
WHERE codeSess = 'SESS-2024-01'
AND noteInscription > (
SELECT AVG(noteInscription)
FROM Inscription i2
WHERE i2.numCINEtu = i1.numCINEtu -- corrélation : même étudiant
AND noteInscription IS NOT NULL
);
NOT EXISTS est-il généralement préféré à NOT IN quand la sous-requête peut contenir des NULL ?Opérateurs ensemblistes
UNION et UNION ALL, conditions de compatibilité, émulation de INTERSECT et EXCEPT/MINUS sous MySQL, diagrammes de Venn, tri et ORDER BY sur les résultats d'union.
- Conditions de compatibilité pour
UNION: même nombre de colonnes, types compatibles UNION— union sans doublons (plus lent, dédoublonnage)UNION ALL— union avec doublons (plus rapide, pas de dédoublonnage)- Émuler
INTERSECTavecINNER JOINouIN - Émuler
EXCEPT/MINUSavecLEFT JOIN ... IS NULLouNOT IN
Pour combiner deux SELECT avec UNION : (1) même nombre de colonnes dans chaque SELECT, (2) colonnes dans le même ordre avec des types compatibles. Les noms des colonnes dans le résultat final viennent du premier SELECT. ORDER BY s'applique au résultat global (après l'union), pas à chaque SELECT individuel.
-- UNION : combiner noms de toutes les personnes (sans doublons)
SELECT nomFormateur AS nom, prenomFormateur AS prenom, 'Formateur' AS role
FROM Formateur
UNION
SELECT nomEtu, prenomEtu, 'Etudiant'
FROM Etudiant;
-- Les noms des colonnes viennent du 1er SELECT
-- Les doublons (même nom+prénom) sont supprimés
-- UNION ALL : garder tous (avec doublons potentiels, plus rapide)
SELECT nomFormateur AS nom, 'Formateur' AS type FROM Formateur
UNION ALL
SELECT nomEtu, 'Etudiant' FROM Etudiant;
-- Plus rapide car pas de déduplication
-- ORDER BY sur le résultat global (placer à la fin)
SELECT nomEtu AS nom, villeEtu AS ville FROM Etudiant
UNION ALL
SELECT nomFormateur, 'Casablanca' FROM Formateur
ORDER BY nom ASC; -- Trie TOUT le résultat
-- Rapport unifié : toutes les activités de 2024
SELECT 'Inscription' AS type_activite, numCINEtu AS reference,
codeSess AS detail, NULL AS note
FROM Inscription
WHERE codeSess LIKE '%2024%'
UNION ALL
SELECT 'Session', codeSess, codeForm, NULL
FROM Session
WHERE YEAR(dateDebutSess) = 2024
ORDER BY type_activite, reference;
MySQL 8.0.31+ supporte INTERSECT et EXCEPT nativement. Sur les versions antérieures, il faut les émuler. Dans tous les cas, connaître les équivalences permet de choisir la solution la plus performante.
-- INTERSECT natif (MySQL 8.0.31+)
-- Étudiants inscrits à SESS-2024-01 ET à SESS-2024-02
SELECT numCINEtu FROM Inscription WHERE codeSess = 'SESS-2024-01'
INTERSECT
SELECT numCINEtu FROM Inscription WHERE codeSess = 'SESS-2024-02';
-- Émulation INTERSECT avec IN (compatible toutes versions MySQL)
SELECT DISTINCT numCINEtu
FROM Inscription
WHERE codeSess = 'SESS-2024-01'
AND numCINEtu IN (
SELECT numCINEtu FROM Inscription WHERE codeSess = 'SESS-2024-02'
);
-- EXCEPT natif (MySQL 8.0.31+)
-- Étudiants inscrits à SESS-2024-01 MAIS PAS à SESS-2024-02
SELECT numCINEtu FROM Inscription WHERE codeSess = 'SESS-2024-01'
EXCEPT
SELECT numCINEtu FROM Inscription WHERE codeSess = 'SESS-2024-02';
-- Émulation EXCEPT avec NOT IN
SELECT DISTINCT numCINEtu
FROM Inscription
WHERE codeSess = 'SESS-2024-01'
AND numCINEtu NOT IN (
SELECT numCINEtu
FROM Inscription
WHERE codeSess = 'SESS-2024-02'
);
-- Émulation EXCEPT avec LEFT JOIN (plus performant sur grandes tables)
SELECT DISTINCT i1.numCINEtu
FROM Inscription i1
LEFT JOIN Inscription i2
ON i1.numCINEtu = i2.numCINEtu AND i2.codeSess = 'SESS-2024-02'
WHERE i1.codeSess = 'SESS-2024-01'
AND i2.numCINEtu IS NULL; -- IS NULL = pas de correspondance à droite
UNION INTERSECT EXCEPT (A - B)
A ┌───┐B A ┌───┐B A ┌───┐B
████████ ░░░██░░ ███░░░░
Tout A + tout B Seulement Seulement dans A,
(sans doublons) l'intersection pas dans B
UNION retourne 8 lignes et UNION ALL retourne 12 lignes. Combien y a-t-il de doublons ?Jointures — INNER, LEFT, RIGHT
Comprendre les jointures SQL — INNER JOIN (correspondances communes), LEFT JOIN (toutes les lignes de gauche + NULL à droite), RIGHT JOIN (toutes les lignes de droite), syntaxes ON et USING, jointures multiples et préfixes de table.
- Pourquoi les jointures : combiner des données de plusieurs tables
INNER JOIN— uniquement les lignes avec correspondance dans les deux tablesLEFT JOIN— toutes les lignes de gauche, NULL si pas de correspondance à droiteRIGHT JOIN— toutes les lignes de droite, NULL si pas de correspondance à gauche- Syntaxe
ONvsUSING, alias de table, jointures multiples
En Merise, les données sont distribuées dans plusieurs tables pour éviter la redondance (3FN). Pour répondre à une question qui touche plusieurs entités (« quel étudiant a eu quelle note dans quelle formation ? »), il faut joindre les tables concernées. Les jointures sont la réponse SQL au passage MLD → requêtes.
-- INNER JOIN basique : étudiants avec leurs notes
SELECT
e.numCINEtu,
e.nomEtu,
e.prenomEtu,
i.codeSess,
i.noteInscription
FROM Etudiant e
INNER JOIN Inscription i ON e.numCINEtu = i.numCINEtu;
-- Seuls les étudiants AVEC au moins une inscription apparaissent
-- Les étudiants sans inscription sont exclus
-- Syntaxe USING (quand les colonnes ont le même nom)
SELECT e.nomEtu, i.noteInscription
FROM Etudiant e
INNER JOIN Inscription i USING (numCINEtu);
-- Equivalent à ON e.numCINEtu = i.numCINEtu
-- JOIN sans INNER (INNER est implicite)
SELECT e.nomEtu, i.noteInscription
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu;
-- Jointure sur 3 tables : étudiant + inscription + session
SELECT
e.nomEtu,
e.prenomEtu,
s.codeSess,
s.dateDebutSess,
i.noteInscription
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu
JOIN Session s ON i.codeSess = s.codeSess;
-- Jointure sur 5 tables : parcours complet
SELECT
e.nomEtu,
e.prenomEtu,
fo.titreForm,
fi.libelleFiliere,
CONCAT(fmt.prenomFormateur, ' ', fmt.nomFormateur) AS formateur,
i.noteInscription
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu
JOIN Session s ON i.codeSess = s.codeSess
JOIN Formation fo ON s.codeForm = fo.codeForm
JOIN Filiere fi ON fo.codeFiliere = fi.codeFiliere
JOIN Formateur fmt ON s.numFormateur = fmt.numFormateur
ORDER BY e.nomEtu, fo.titreForm;
-- LEFT JOIN : TOUS les étudiants, avec ou sans inscription
SELECT
e.numCINEtu,
e.nomEtu,
e.prenomEtu,
i.codeSess, -- NULL si pas d'inscription
i.noteInscription -- NULL si pas d'inscription
FROM Etudiant e
LEFT JOIN Inscription i ON e.numCINEtu = i.numCINEtu;
-- TOUS les étudiants apparaissent, y compris ceux sans inscription
-- Compter les inscriptions par étudiant (y compris 0)
SELECT
e.numCINEtu,
e.nomEtu,
COUNT(i.codeSess) AS nb_inscriptions -- COUNT ignore les NULL
FROM Etudiant e
LEFT JOIN Inscription i ON e.numCINEtu = i.numCINEtu
GROUP BY e.numCINEtu, e.nomEtu
ORDER BY nb_inscriptions DESC;
-- Anti-jointure : étudiants SANS aucune inscription
SELECT e.numCINEtu, e.nomEtu
FROM Etudiant e
LEFT JOIN Inscription i ON e.numCINEtu = i.numCINEtu
WHERE i.numCINEtu IS NULL; -- IS NULL = pas de correspondance à droite
-- LEFT JOIN sur 3 tables : toutes les formations avec ou sans session
SELECT
fi.libelleFiliere,
fo.titreForm,
COUNT(s.codeSess) AS nb_sessions
FROM Filiere fi
LEFT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
LEFT JOIN Session s ON fo.codeForm = s.codeForm
GROUP BY fi.codeFiliere, fi.libelleFiliere, fo.codeForm, fo.titreForm
ORDER BY fi.libelleFiliere, fo.titreForm;
-- RIGHT JOIN : équivalent au LEFT JOIN inversé
-- Toutes les sessions avec ou sans formateur assigné
SELECT
s.codeSess,
f.nomFormateur -- NULL si aucun formateur
FROM Formateur f
RIGHT JOIN Session s ON f.numFormateur = s.numFormateur;
-- Équivalent à : Session LEFT JOIN Formateur
INNER JOIN LEFT JOIN RIGHT JOIN
A ┌───┐B A ┌───┐B A ┌───┐B
░░░██░░ █████░░ ░░░████
Intersection Tout A + Tout B +
seulement intersection intersection
LEFT JOIN + WHERE B.col IS NULL → Uniquement dans A (anti-jointure)
A ┌───┐B
███░░░░ Tout A sauf l'intersection
SELECT COUNT(*) FROM Etudiant e LEFT JOIN Inscription i ON e.numCINEtu = i.numCINEtu — combien de lignes retourne ce SELECT (sans GROUP BY) ?Jointures — CROSS, SELF, FULL OUTER
CROSS JOIN (produit cartésien), SELF JOIN (auto-jointure avec alias obligatoire), FULL OUTER JOIN (émulation MySQL avec UNION), et synthèse des 6 types de jointures avec comparaison.
CROSS JOIN— produit cartésien (chaque ligne de A x chaque ligne de B)SELF JOIN— jointure d'une table sur elle-même, alias obligatoiresFULL OUTER JOIN— toutes les lignes des deux tables (émulation MySQL)- Tableau récapitulatif des 6 types de jointures
-- CROSS JOIN : chaque ligne de A combinée avec chaque ligne de B
SELECT
e.nomEtu,
s.codeSess
FROM Etudiant e
CROSS JOIN Session s;
-- 6 étudiants x 4 sessions = 24 lignes
-- Utile pour générer toutes les combinaisons possibles
-- Cas d'usage : générer une grille de disponibilité
-- (toutes les paires formateur-formation possibles)
SELECT
f.nomFormateur,
fm.titreForm
FROM Formateur f
CROSS JOIN Formation fm
ORDER BY f.nomFormateur, fm.titreForm;
-- 4 formateurs x 5 formations = 20 lignes
-- Attention : CROSS JOIN implicite (vieille syntaxe à éviter)
SELECT e.nomEtu, s.codeSess FROM Etudiant e, Session s;
-- Equivalent à CROSS JOIN mais moins lisible
Le SELF JOIN joint une table avec elle-même, avec deux alias différents. Il sert à comparer des lignes entre elles au sein de la même table : trouver des paires, gérer des hiérarchies (employé + manager dans la même table), ou détecter des doublons.
-- Trouver les paires d'étudiants de la même ville
SELECT
e1.nomEtu AS etudiant1,
e2.nomEtu AS etudiant2,
e1.villeEtu
FROM Etudiant e1
INNER JOIN Etudiant e2
ON e1.villeEtu = e2.villeEtu
AND e1.numCINEtu < e2.numCINEtu -- éviter les doublons et les auto-paires
ORDER BY e1.villeEtu;
-- Étudiants inscrits dans les mêmes sessions
SELECT
i1.numCINEtu AS etudiant1,
i2.numCINEtu AS etudiant2,
i1.codeSess
FROM Inscription i1
INNER JOIN Inscription i2
ON i1.codeSess = i2.codeSess
AND i1.numCINEtu < i2.numCINEtu;
-- Exemple hiérarchie : table Organisation (manager référence un autre employé)
-- CREATE TABLE Organisation (id INT PK, nom VARCHAR, manager_id INT FK→id)
-- SELECT e.nom AS employe, m.nom AS manager
-- FROM Organisation e
-- LEFT JOIN Organisation m ON e.manager_id = m.id;
-- MySQL ne supporte pas FULL OUTER JOIN nativement
-- Émulation : LEFT JOIN UNION RIGHT JOIN
-- Toutes les formations et toutes les filières, avec ou sans correspondance
SELECT
fi.libelleFiliere,
fo.titreForm
FROM Filiere fi
LEFT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
UNION
SELECT
fi.libelleFiliere,
fo.titreForm
FROM Filiere fi
RIGHT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere;
-- FULL OUTER ANTI-JOIN (lignes sans correspondance des deux côtés)
SELECT fi.libelleFiliere, fo.titreForm
FROM Filiere fi
LEFT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
WHERE fo.codeFiliere IS NULL -- Filières sans formation
UNION
SELECT fi.libelleFiliere, fo.titreForm
FROM Filiere fi
RIGHT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
WHERE fi.codeFiliere IS NULL; -- Formations sans filière (ne devrait pas exister)
| Type | Lignes retournées | NULL | Usage SkolaBase |
|---|---|---|---|
| INNER JOIN | Seulement les lignes avec correspondance des deux côtés | Aucun | Étudiants avec leurs notes |
| LEFT JOIN | Toutes les lignes de gauche + correspondances à droite | Colonnes droite = NULL si pas de correspondance | Tous les étudiants avec ou sans inscription |
| RIGHT JOIN | Toutes les lignes de droite + correspondances à gauche | Colonnes gauche = NULL si pas de correspondance | Toutes les sessions avec ou sans formateur |
| CROSS JOIN | Toutes les combinaisons (N x M) | Aucun | Grille disponibilité formateurs/formations |
| SELF JOIN | Lignes de la même table comparées entre elles | Selon le type (INNER ou LEFT) | Paires d'étudiants de la même ville |
| FULL OUTER | Toutes les lignes des deux tables | NULL des deux côtés selon correspondance | Émulation MySQL : LEFT ∪ RIGHT |
Sauvegarde & Restauration
Exporter une base de données avec mysqldump (structure seule, données seules, tout), options utiles, restauration avec SOURCE et via Workbench, stratégies de sauvegarde pour SkolaBase.
- Comprendre l'importance des sauvegardes et les stratégies (complète, incrémentale)
mysqldump— exporter structure seule (--no-data), données seules (--no-create-info), tout- Options utiles :
--single-transaction,--routines,--triggers - Restaurer avec
mysql < fichier.sqletSOURCE fichier.sql - Sauvegarde et restauration graphique via MySQL Workbench
mysqldump est un outil en ligne de commande fourni avec MySQL. Il génère un fichier texte .sql contenant les instructions SQL nécessaires pour recréer la structure et/ou les données de la base. Ce fichier est portable : il peut être restauré sur n'importe quel serveur MySQL compatible.
# Sauvegarder TOUT (structure + données) — la plus courante
mysqldump -u root -p skolabase > skolabase_backup_2024.sql
# -u root : utilisateur MySQL
# -p : demander le mot de passe
# > skolabase_backup_2024.sql : redirection vers le fichier
# Structure SEULEMENT (sans les données — pour recréer le schéma)
mysqldump -u root -p --no-data skolabase > skolabase_structure.sql
# Données SEULEMENT (sans CREATE TABLE — pour recharger des données)
mysqldump -u root -p --no-create-info skolabase > skolabase_data.sql
# Une seule table
mysqldump -u root -p skolabase Inscription > inscription_backup.sql
# Plusieurs tables spécifiques
mysqldump -u root -p skolabase Etudiant Inscription > etudiants_inscriptions.sql
# Plusieurs bases de données
mysqldump -u root -p --databases skolabase skolabase_test > multi_backup.sql
# TOUTES les bases (y compris mysql, information_schema)
mysqldump -u root -p --all-databases > all_databases.sql
# Options recommandées pour la production
mysqldump -u root -p \
--single-transaction \ # Dump cohérent sans verrouiller les tables (InnoDB)
--routines \ # Inclure les procédures stockées et fonctions
--triggers \ # Inclure les triggers
--events \ # Inclure les événements planifiés
--hex-blob \ # Données binaires en hexadécimal
skolabase > skolabase_prod_$(date +%Y%m%d).sql
# Nommage avec date : skolabase_prod_20241115.sql
-- MySQL dump 8.0.36
-- Host: localhost Database: skolabase
-- ------------------------------------------------------
CREATE DATABASE IF NOT EXISTS `skolabase`;
USE `skolabase`;
DROP TABLE IF EXISTS `Filiere`;
CREATE TABLE `Filiere` (
`codeFiliere` CHAR(6) NOT NULL,
`libelleFiliere` VARCHAR(80) NOT NULL,
PRIMARY KEY (`codeFiliere`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
LOCK TABLES `Filiere` WRITE;
INSERT INTO `Filiere` VALUES
('INF','Informatique'),
('GES','Gestion et Commerce');
UNLOCK TABLES;
# Méthode 1 : redirection en ligne de commande
mysql -u root -p skolabase < skolabase_backup_2024.sql
# Restaure toutes les tables et données dans la base skolabase
# Si la base n'existe pas encore
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS skolabase CHARACTER SET utf8mb4;"
mysql -u root -p skolabase < skolabase_backup_2024.sql
# Méthode 2 : à l'intérieur de MySQL Shell
mysql -u root -p
-- Dans MySQL :
SOURCE /chemin/vers/skolabase_backup_2024.sql;
-- ou sur Windows :
SOURCE C:/backup/skolabase_backup_2024.sql;
# Vérifier que la restauration a fonctionné
mysql -u root -p -e "USE skolabase; SHOW TABLES; SELECT COUNT(*) FROM Etudiant;"
① Ouvrir Workbench et se connecter à SkolaBase_Local
② Server → Data Import
③ Choisir "Import from Self-Contained File"
④ Cliquer ... et sélectionner le fichier .sql de sauvegarde
⑤ Default Target Schema : choisir skolabase
(ou "New..." pour créer une nouvelle base)
⑥ Cliquer "Start Import" → attendre la fin
⑦ Vérifier dans l'onglet "Object Browser" que les tables sont bien présentes
Si le fichier .sql contient DROP TABLE IF EXISTS avant chaque CREATE TABLE (comportement par défaut de mysqldump), et que les tables ont des FK, la restauration peut échouer si l'ordre n'est pas correct. mysqldump génère automatiquement SET FOREIGN_KEY_CHECKS = 0; au début du fichier pour éviter ce problème. Vérifier que cette ligne est présente dans le fichier de restauration.
mysqldump --single-transaction est recommandé pour InnoDB. Pourquoi ?Import & Export de données
Importer un fichier CSV dans une table MySQL avec LOAD DATA INFILE, exporter les résultats d'un SELECT dans un fichier avec SELECT INTO OUTFILE, et utiliser l'assistant d'import/export de MySQL Workbench.
LOAD DATA INFILE— importer un fichier CSV dans une table- Configurer le séparateur, le qualificateur de texte et la fin de ligne
- Ignorer l'en-tête, mapper les colonnes
SELECT ... INTO OUTFILE— exporter une table ou un SELECT en CSV- Import/export graphique via MySQL Workbench Table Data Import Wizard
numCINEtu,nomEtu,prenomEtu,dateNaissEtu,villeEtu,telEtu
A1234567,Alaoui,Karim,2002-05-15,Casablanca,0612345678
B7654321,Bennani,Sara,2001-11-22,Rabat,0698765432
C2468135,Chraibi,Youssef,2003-01-08,Marrakech,
-- Import basique
LOAD DATA INFILE '/var/lib/mysql-files/etudiants_import.csv'
INTO TABLE Etudiant
FIELDS TERMINATED BY ',' -- Séparateur de colonnes
ENCLOSED BY '"' -- Qualificateur de texte (guillemets)
LINES TERMINATED BY '\n' -- Fin de ligne (Unix) ou '\r\n' (Windows)
IGNORE 1 LINES -- Ignorer la ligne d'en-tête
(numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu);
-- Import avec transformation : majuscules sur le nom
LOAD DATA INFILE '/var/lib/mysql-files/etudiants_import.csv'
INTO TABLE Etudiant
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(@cin, @nom, @prenom, @naissance, @ville, @tel)
SET
numCINEtu = @cin,
nomEtu = UPPER(@nom), -- Forcer les majuscules
prenomEtu = @prenom,
dateNaissEtu = STR_TO_DATE(@naissance, '%Y-%m-%d'),
villeEtu = COALESCE(NULLIF(@ville, ''), 'Casablanca'),
telEtu = NULLIF(@tel, ''); -- '' → NULL
-- Vérifier le résultat
SELECT * FROM Etudiant LIMIT 5;
MySQL n'accepte les fichiers que depuis un répertoire sécurisé (secure_file_priv). Sous Windows : vérifier SHOW VARIABLES LIKE 'secure_file_priv'. Le résultat indique le seul dossier autorisé. Copier le fichier CSV dans ce dossier avant d'exécuter LOAD DATA.
-- Exporter la table Etudiant en CSV
SELECT *
INTO OUTFILE '/var/lib/mysql-files/etudiants_export.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM Etudiant;
-- Exporter avec en-tête (astuce : UNION ALL avec les noms de colonnes)
SELECT 'numCINEtu', 'nomEtu', 'prenomEtu', 'dateNaissEtu', 'villeEtu'
UNION ALL
SELECT numCINEtu, nomEtu, prenomEtu,
DATE_FORMAT(dateNaissEtu, '%d/%m/%Y'), villeEtu
INTO OUTFILE '/var/lib/mysql-files/etudiants_avec_entete.csv'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM Etudiant;
-- Exporter un résultat de jointure
SELECT
e.numCINEtu,
CONCAT(e.prenomEtu, ' ', e.nomEtu) AS nom_complet,
fo.titreForm,
i.noteInscription
INTO OUTFILE '/var/lib/mysql-files/resultats_etudiants.csv'
FIELDS TERMINATED BY ';' ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu
JOIN Session s ON i.codeSess = s.codeSess
JOIN Formation fo ON s.codeForm = fo.codeForm
ORDER BY e.nomEtu;
① Clic droit sur la table Etudiant dans l'Object Browser
② Choisir "Table Data Import Wizard"
③ Cliquer "Browse" et sélectionner le fichier CSV
④ Choisir l'encodage (UTF-8 pour les accents et caractères arabes)
⑤ Configurer le séparateur (virgule, point-virgule...)
⑥ Mapper les colonnes du CSV aux colonnes de la table
⑦ Cliquer "Next" puis "Execute Import"
⑧ Vérifier le rapport : nb lignes importées, erreurs éventuelles
LOAD DATA INFILE importe 100 lignes mais 3 ont un numCINEtu déjà présent dans la table (doublon PK). Que se passe-t-il par défaut ?Gestion des utilisateurs MySQL
Créer des comptes utilisateurs MySQL (CREATE USER), gérer les hôtes autorisés, modifier les mots de passe (ALTER USER), lister les comptes dans mysql.user, supprimer un compte (DROP USER) et tester les connexions.
- Comprendre la notion d'utilisateur MySQL : login@hôte
CREATE USER 'nom'@'hôte' IDENTIFIED BY 'mot_de_passe'- Hôtes :
localhost,%(tous), adresse IP spécifique - Lister les comptes :
SELECT ... FROM mysql.user ALTER USER— changer le mot de passe, expirationDROP USER— supprimer un compte
Un compte MySQL est toujours identifié par deux composants : le nom d'utilisateur ET l'hôte depuis lequel il se connecte. 'karim'@'localhost' et 'karim'@'%' sont deux comptes différents avec potentiellement des privilèges différents. '%' = accepte depuis n'importe quel hôte.
-- Utilisateur administrateur SkolaBase (connexion locale seulement)
CREATE USER IF NOT EXISTS 'skola_admin'@'localhost'
IDENTIFIED BY 'SkolaAdmin_2024!';
-- Utilisateur application (lecture + écriture, depuis l'application web)
CREATE USER IF NOT EXISTS 'skola_app'@'localhost'
IDENTIFIED BY 'SkolaApp_2024!';
-- Utilisateur lecture seule (pour les rapports, depuis n'importe où)
CREATE USER IF NOT EXISTS 'skola_reader'@'%'
IDENTIFIED BY 'SkolaReader_2024!';
-- Vérifier la création
SELECT User, Host, plugin, password_expired
FROM mysql.user
WHERE User LIKE 'skola%';
-- Résultat attendu :
-- User | Host | plugin | password_expired
-- skola_admin | localhost | caching_sha2_password | N
-- skola_app | localhost | caching_sha2_password | N
-- skola_reader | % | caching_sha2_password | N
-- Changer le mot de passe
ALTER USER 'skola_app'@'localhost'
IDENTIFIED BY 'NouveauMdp_2024!';
-- Forcer le changement de mot de passe à la prochaine connexion
ALTER USER 'skola_reader'@'%'
PASSWORD EXPIRE;
-- Expiration automatique tous les 90 jours
ALTER USER 'skola_app'@'localhost'
PASSWORD EXPIRE INTERVAL 90 DAY;
-- Désactiver le compte (sans le supprimer)
ALTER USER 'skola_reader'@'%' ACCOUNT LOCK;
-- Réactiver le compte
ALTER USER 'skola_reader'@'%' ACCOUNT UNLOCK;
-- Changer son propre mot de passe (connecté comme skola_app)
ALTER USER CURRENT_USER() IDENTIFIED BY 'MonNouveauMdp!';
-- Supprimer un compte (et tous ses privilèges)
DROP USER IF EXISTS 'skola_reader'@'%';
-- Tester la connexion depuis le terminal
-- mysql -u skola_app -p skolabase
-- Entrer le mot de passe → doit se connecter
-- Voir le compte courant
SELECT CURRENT_USER();
'karim'@'localhost' et 'karim'@'%' — si les deux comptes existent, depuis une connexion locale (localhost), quel compte MySQL utilise-t-il ?Privilèges & Sécurité
Accorder des privilèges avec GRANT (6 niveaux de granularité), tableau des privilèges MySQL, révoquer avec REVOKE, vérifier les droits avec SHOW GRANTS, et stratégie de sécurité pour SkolaBase (principe du moindre privilège).
- Les 6 niveaux de privilèges MySQL : Global, BDD, Table, Colonne, Routine, Proxy
GRANT privilege ON niveau TO utilisateur- Privilèges courants : SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALL
REVOKE— retirer des privilègesSHOW GRANTS FOR— voir les privilèges d'un utilisateur- Principe du moindre privilège appliqué à SkolaBase
| Niveau | Syntaxe ON | Granularité | Exemple SkolaBase |
|---|---|---|---|
| Global | ON *.* | S'applique à toutes les BDD du serveur | Administrateur système MySQL |
| BDD | ON skolabase.* | S'applique à toutes les tables d'une BDD | Administrateur SkolaBase uniquement |
| Table | ON skolabase.Etudiant | S'applique à une seule table | Secrétaire ne gère que les étudiants |
| Colonne | ON table (colonne) | S'applique à une ou plusieurs colonnes | Cacher la note à certains utilisateurs |
| Routine | ON PROCEDURE/FUNCTION | S'applique à une procédure stockée | Accès à une procédure spécifique |
| Proxy | PROXY ON | Usurper les droits d'un autre utilisateur | Rare — gestion avancée |
| Privilège | Permet | Usage SkolaBase |
|---|---|---|
SELECT | Lire les données | Tous les utilisateurs |
INSERT | Ajouter des lignes | Application, admin |
UPDATE | Modifier des lignes | Application, admin |
DELETE | Supprimer des lignes | Admin uniquement |
CREATE | Créer des tables/BDD | Admin uniquement |
DROP | Supprimer tables/BDD | Admin uniquement |
ALTER | Modifier la structure | Admin uniquement |
INDEX | Créer/supprimer des index | Admin uniquement |
GRANT OPTION | Transmettre ses privilèges à d'autres | Jamais pour les comptes applicatifs |
ALL PRIVILEGES | Tous les privilèges | root et admins système uniquement |
-- skola_admin : tous les droits sur SkolaBase uniquement
GRANT ALL PRIVILEGES ON skolabase.* TO 'skola_admin'@'localhost';
-- skola_app : droits CRUD (pas DROP, CREATE, ALTER)
GRANT SELECT, INSERT, UPDATE, DELETE
ON skolabase.*
TO 'skola_app'@'localhost';
-- skola_reader : lecture seule sur toute la base
GRANT SELECT ON skolabase.* TO 'skola_reader'@'%';
-- Niveau table : secrétaire gère uniquement les étudiants
GRANT SELECT, INSERT, UPDATE ON skolabase.Etudiant
TO 'secretaire'@'localhost';
GRANT SELECT ON skolabase.Inscription
TO 'secretaire'@'localhost';
-- Niveau colonne : cacher les notes à certains utilisateurs
GRANT SELECT (numCINEtu, codeSess) ON skolabase.Inscription
TO 'skola_view'@'localhost';
-- skola_view peut voir numCINEtu et codeSess mais PAS noteInscription
-- Appliquer immédiatement (MySQL 8 recharge automatiquement)
FLUSH PRIVILEGES;
-- Note : FLUSH PRIVILEGES est nécessaire seulement si on modifie
-- directement les tables mysql.user (à éviter)
-- Vérifier les droits accordés
SHOW GRANTS FOR 'skola_app'@'localhost';
-- Résultat :
-- GRANT SELECT, INSERT, UPDATE, DELETE ON `skolabase`.* TO 'skola_app'@'localhost'
-- Retirer le droit DELETE à skola_app
REVOKE DELETE ON skolabase.* FROM 'skola_app'@'localhost';
-- Retirer TOUS les droits
REVOKE ALL PRIVILEGES ON skolabase.* FROM 'skola_reader'@'%';
-- Vérifier les droits de tous les utilisateurs SkolaBase
SELECT
User,
Host,
Select_priv,
Insert_priv,
Update_priv,
Delete_priv,
Create_priv,
Drop_priv
FROM mysql.user
WHERE User LIKE 'skola%';
-- Lister tous les GRANT sur une base spécifique
SELECT *
FROM information_schema.SCHEMA_PRIVILEGES
WHERE TABLE_SCHEMA = 'skolabase';
-- Résumé de la stratégie de sécurité SkolaBase :
-- root → NE JAMAIS utiliser pour l'application
-- skola_admin → Admins techniques (migrations, sauvegardes)
-- skola_app → Application web (SELECT/INSERT/UPDATE/DELETE)
-- skola_reader → Rapports, analyses (SELECT uniquement)
-- secretaire → Interface secrétariat (Etudiant + Inscription SELECT/INSERT/UPDATE)
Chaque compte ne doit avoir que les privilèges strictement nécessaires à sa fonction. Un compte applicatif (application web) n'a jamais besoin de DROP ou CREATE — si la connexion est compromisée, l'attaquant ne peut pas détruire la structure. Un utilisateur de rapports n'a besoin que de SELECT. Cette segmentation limite considérablement les dommages en cas de compromission.
GRANT ALL PRIVILEGES ON *.* TO 'skola_app'@'localhost' est configuré pour le compte applicatif. Quel est le risque de sécurité principal ?REVOKE ALL PRIVILEGES ON skolabase.* FROM 'skola_app'@'localhost' et DROP USER 'skola_app'@'localhost' ?SkolaBase — CDC & Analyse Merise complète
Cahier des charges du centre de formation SkolaBase, extraction des règles de gestion, dictionnaire de données complet, graphe des DF, MCD et MLD — la démarche Merise complète de A à Z en un seul cas.
SkolaBase est la base de données d'un centre de formation marocain. Ce projet mobilise toutes les compétences du cours : Merise (RG, DD, DF, MCD, MLD), MySQL (DDL, DML, DQL, jointures, sous-requêtes) et administration. Chaque étape s'appuie sur la précédente.
CAHIER DES CHARGES — SkolaBase
Centre de Formation Professionnelle — Maroc
==============================================
1. CONTEXTE
Le centre SkolaBase dispense des formations professionnelles continues
pour des techniciens et cadres marocains. Il gère actuellement ses
données sur registres papier et souhaite une base de données MySQL.
2. ENTITES IDENTIFIEES
● ETUDIANT : toute personne inscrite à une session de formation.
Chaque étudiant possède un numéro CIN unique (lettre + 7 chiffres).
● FORMATEUR : intervenant qui anime les sessions.
● FORMATION : programme pédagogique avec titre, durée et niveau.
● SESSION : occurrence décalée d'une formation dans le temps et l'espace.
● FILIERE : domaine regroupant plusieurs formations (INF, GES, ELE, BTP).
3. REGLES DE GESTION
RG1 : Un étudiant peut s'inscrire à plusieurs sessions. Une session
peut accueillir plusieurs étudiants. (N:N)
RG2 : Chaque inscription d'un étudiant à une session donne lieu à
une note (0 à 20, peut être absente si non encore saisie).
RG3 : Chaque session est animée par exactement un formateur.
Un formateur peut animer plusieurs sessions. (1:N)
RG4 : Chaque session porte sur exactement une formation.
Une formation peut donner lieu à plusieurs sessions. (1:N)
RG5 : Chaque formation appartient à exactement une filière.
Une filière regroupe une ou plusieurs formations. (1:N)
4. PERIMETRE
Inclus : gestion inscriptions, sessions, notes, formateurs, formations, filières
Exclus : paie des formateurs, comptabilité générale, app mobile
5. CONTRAINTES TECHNIQUES
SGBD : MySQL 8.0 — moteur InnoDB — charset utf8mb4
Serveur : Windows Server local ou Linux
Accès simultanés : 10 utilisateurs maximum
| Code | Désignation | T | Taille | Observations | Entité |
|---|---|---|---|---|---|
numCINEtu | N° CIN étudiant | AN | 8 | PK, format A1234567 | ETUDIANT |
nomEtu | Nom étudiant | A | 50 | NN | ETUDIANT |
prenomEtu | Prénom étudiant | A | 50 | NN | ETUDIANT |
dateNaissEtu | Date de naissance | D | — | NULL accepté | ETUDIANT |
villeEtu | Ville résidence | A | 50 | Défaut Casablanca | ETUDIANT |
telEtu | Téléphone | AN | 15 | NULL accepté | ETUDIANT |
numFormateur | ID formateur | N | 4 | PK AUTO_INCREMENT | FORMATEUR |
nomFormateur | Nom formateur | A | 50 | NN | FORMATEUR |
prenomFormateur | Prénom formateur | A | 50 | NN | FORMATEUR |
specialiteForm | Spécialité | A | 80 | NULL accepté | FORMATEUR |
codeSess | Code session | AN | 10 | PK, format SESS-AAAA-NN | SESSION |
dateDebutSess | Date début | D | — | NN | SESSION |
dateFinSess | Date fin | D | — | NULL accepté | SESSION |
lieuSess | Lieu session | AN | 100 | NULL accepté | SESSION |
codeForm | Code formation | AN | 10 | PK unique | FORMATION |
titreForm | Titre formation | AN | 100 | NN | FORMATION |
dureeForm | Durée (heures) | N | 3 | Entier positif | FORMATION |
niveauForm | Niveau requis | A | 20 | ENUM Bac/Bac+2/Bac+3/Tech. | FORMATION |
codeFiliere | Code filière | AN | 6 | PK, ex: INF, GES | FILIERE |
libelleFiliere | Libellé filière | A | 80 | NN | FILIERE |
noteInscription | Note obtenue | N | 4,2 | 0.00–20.00, NULL si non saisie | INSCRIPTION |
numCINEtu ──► nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu
numFormateur ──► nomFormateur, prenomFormateur, specialiteForm
codeSess ──► dateDebutSess, dateFinSess, lieuSess, numFormateur (FK), codeForm (FK)
codeForm ──► titreForm, dureeForm, niveauForm, codeFiliere (FK)
codeFiliere ──► libelleFiliere
DF composée :
numCINEtu + codeSess ──► noteInscription (association INSCRIPTION porteuse)
ETUDIANT (0,N) ◆─ INSCRIPTION (porteuse: noteInscription) ─◆ (1,N) SESSION
R3 → table pivot INSCRIPTION
SESSION (1,N) ◆─ ANIME (RG3) ─◆ (1,1) FORMATEUR
R2 → FK numFormateur dans SESSION
SESSION (1,N) ◆─ PORTE SUR (RG4) ─◆ (1,1) FORMATION
R2 → FK codeForm dans SESSION
FORMATION (1,N) ◆─ APPARTIENT A (RG5) ─◆ (1,1) FILIERE
R2 → FK codeFiliere dans FORMATION
FILIERE (#codeFiliere, libelleFiliere)
FORMATION (#codeForm, titreForm, dureeForm, niveauForm,
*codeFiliere → FILIERE.codeFiliere)
FORMATEUR (#numFormateur AI, nomFormateur, prenomFormateur, specialiteForm)
SESSION (#codeSess, dateDebutSess, dateFinSess, lieuSess,
*numFormateur → FORMATEUR.numFormateur,
*codeForm → FORMATION.codeForm)
ETUDIANT (#numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu)
INSCRIPTION(#numCINEtu → ETUDIANT.numCINEtu,
#codeSess → SESSION.codeSess,
noteInscription)
CLE COMPOSEE : (numCINEtu, codeSess)
Étape 1 — Création de la BDD (DDL complet)
Script SQL complet de création de la base SkolaBase : CREATE DATABASE, les 6 tables dans le bon ordre, toutes les contraintes (PK, FK nommées, CHECK, DEFAULT), index et commentaires.
-- ============================================================
-- SkolaBase — Script DDL complet
-- Centre de formation marocain — MySQL 8.0
-- ============================================================
DROP DATABASE IF EXISTS skolabase;
CREATE DATABASE skolabase
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci
COMMENT 'Base de données centre de formation SkolaBase';
USE skolabase;
-- ── Table 1 : FILIERE (aucune FK — créer en premier) ────────
CREATE TABLE IF NOT EXISTS Filiere (
codeFiliere CHAR(6) NOT NULL,
libelleFiliere VARCHAR(80) NOT NULL,
CONSTRAINT pk_Filiere PRIMARY KEY (codeFiliere)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
COMMENT = 'Filières de formation (INF, GES, ELE, BTP)';
-- ── Table 2 : FORMATION ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS Formation (
codeForm VARCHAR(10) NOT NULL,
titreForm VARCHAR(100) NOT NULL,
dureeForm SMALLINT UNSIGNED NOT NULL COMMENT 'Durée en heures',
niveauForm ENUM('Technicien','Bac','Bac+2','Bac+3') NULL,
codeFiliere CHAR(6) NOT NULL,
CONSTRAINT pk_Formation PRIMARY KEY (codeForm),
CONSTRAINT fk_Form_Fil FOREIGN KEY (codeFiliere)
REFERENCES Filiere(codeFiliere)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT chk_Duree CHECK (dureeForm > 0)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- ── Table 3 : FORMATEUR ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS Formateur (
numFormateur INT NOT NULL AUTO_INCREMENT,
nomFormateur VARCHAR(50) NOT NULL,
prenomFormateur VARCHAR(50) NOT NULL,
specialiteForm VARCHAR(80) NULL,
CONSTRAINT pk_Formateur PRIMARY KEY (numFormateur)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- ── Table 4 : SESSION ───────────────────────────────────────
CREATE TABLE IF NOT EXISTS Session (
codeSess VARCHAR(10) NOT NULL,
dateDebutSess DATE NOT NULL,
dateFinSess DATE NULL,
lieuSess VARCHAR(100) NULL,
numFormateur INT NOT NULL,
codeForm VARCHAR(10) NOT NULL,
CONSTRAINT pk_Session PRIMARY KEY (codeSess),
CONSTRAINT fk_Sess_Format FOREIGN KEY (numFormateur)
REFERENCES Formateur(numFormateur)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_Sess_Form FOREIGN KEY (codeForm)
REFERENCES Formation(codeForm)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT chk_DatesSess CHECK (dateFinSess IS NULL OR dateFinSess >= dateDebutSess),
INDEX idx_Sess_Form (codeForm),
INDEX idx_Sess_Debut (dateDebutSess)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- ── Table 5 : ETUDIANT ──────────────────────────────────────
CREATE TABLE IF NOT EXISTS Etudiant (
numCINEtu CHAR(8) NOT NULL COMMENT 'Format : lettre + 7 chiffres',
nomEtu VARCHAR(50) NOT NULL,
prenomEtu VARCHAR(50) NOT NULL,
dateNaissEtu DATE NULL,
villeEtu VARCHAR(50) NOT NULL DEFAULT 'Casablanca',
telEtu VARCHAR(15) NULL,
CONSTRAINT pk_Etudiant PRIMARY KEY (numCINEtu),
CONSTRAINT chk_CIN
CHECK (numCINEtu REGEXP '^[A-Z][0-9]{7}$') ENFORCED,
CONSTRAINT chk_DateNaiss
CHECK (dateNaissEtu IS NULL OR dateNaissEtu < CURDATE()) ENFORCED,
INDEX idx_Etu_Ville (villeEtu)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- ── Table 6 : INSCRIPTION (créer en dernier — FK vers Etudiant + Session) ─
CREATE TABLE IF NOT EXISTS Inscription (
numCINEtu CHAR(8) NOT NULL,
codeSess VARCHAR(10) NOT NULL,
noteInscription DECIMAL(4,2) NULL COMMENT 'Note sur 20',
CONSTRAINT pk_Inscription PRIMARY KEY (numCINEtu, codeSess),
CONSTRAINT fk_Insc_Etu FOREIGN KEY (numCINEtu)
REFERENCES Etudiant(numCINEtu)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_Insc_Sess FOREIGN KEY (codeSess)
REFERENCES Session(codeSess)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT chk_Note
CHECK (noteInscription IS NULL OR
(noteInscription >= 0 AND noteInscription <= 20)) ENFORCED
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- Vérification
SHOW TABLES;
SELECT TABLE_NAME, ENGINE, TABLE_ROWS, TABLE_COMMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'skolabase';
Étape 2 — Peuplement des données (DML)
Script DML complet pour peupler SkolaBase avec des données marocaines réalistes — 4 filières, 5 formations, 4 formateurs, 4 sessions, 6 étudiants et 7 inscriptions avec notes.
USE skolabase;
-- ── 1. Filières ────────────────────────────────────────────
INSERT INTO Filiere (codeFiliere, libelleFiliere) VALUES
('INF', 'Informatique'),
('GES', 'Gestion et Commerce'),
('ELE', 'Electronique et Electrotechnique'),
('BTP', 'Batiment et Travaux Publics');
-- ── 2. Formations ───────────────────────────────────────────
INSERT INTO Formation (codeForm, titreForm, dureeForm, niveauForm, codeFiliere) VALUES
('DEV-WEB', 'Developpement Web Full Stack', 120, 'Bac+2', 'INF'),
('DEV-MOB', 'Developpement Mobile Android', 80, 'Bac+2', 'INF'),
('BDD-ADM', 'Administration des Bases de Donnees', 100, 'Bac+3', 'INF'),
('CPT-GEN', 'Comptabilite Generale', 90, 'Bac', 'GES'),
('MKT-DIG', 'Marketing Digital', 60, 'Bac+2', 'GES');
-- ── 3. Formateurs ───────────────────────────────────────────
INSERT INTO Formateur (nomFormateur, prenomFormateur, specialiteForm) VALUES
('Alaoui', 'Karim', 'Developpement Web, PHP, Laravel'),
('Bennani', 'Nadia', 'Bases de donnees, MySQL, Oracle'),
('Chraibi', 'Hassan', 'Reseaux, Administration systeme'),
('Idrissi', 'Leila', 'Marketing digital, SEO');
-- ── 4. Sessions ─────────────────────────────────────────────
INSERT INTO Session (codeSess, dateDebutSess, dateFinSess, lieuSess, numFormateur, codeForm) VALUES
('SESS-2024-01', '2024-09-02', '2024-12-20', 'Salle A101 - Casablanca', 1, 'DEV-WEB'),
('SESS-2024-02', '2024-09-09', '2024-11-29', 'Salle B205 - Rabat', 2, 'BDD-ADM'),
('SESS-2024-03', '2024-10-07', '2024-12-13', 'Salle A102 - Casablanca', 1, 'DEV-MOB'),
('SESS-2025-01', '2025-01-13', '2025-04-04', 'Salle A101 - Casablanca', 1, 'DEV-WEB');
-- ── 5. Etudiants ────────────────────────────────────────────
INSERT INTO Etudiant (numCINEtu, nomEtu, prenomEtu, dateNaissEtu, villeEtu, telEtu) VALUES
('A1234567', 'Alaoui', 'Karim', '2002-05-15', 'Casablanca', '0612345678'),
('B7654321', 'Bennani', 'Sara', '2001-11-22', 'Rabat', '0698765432'),
('C2468135', 'Chraibi', 'Youssef', '2003-01-08', 'Marrakech', '0635791246'),
('D1357924', 'Idrissi', 'Fatima', '2002-08-30', 'Fes', '0674513892'),
('E9876543', 'Mansouri', 'Amine', '2001-03-17', 'Casablanca', '0656789012'),
('F3692581', 'Tazi', 'Zineb', '2003-06-25', 'Tanger', '0678901234');
-- ── 6. Inscriptions ─────────────────────────────────────────
INSERT INTO Inscription (numCINEtu, codeSess, noteInscription) VALUES
('A1234567', 'SESS-2024-01', 14.50),
('B7654321', 'SESS-2024-01', 16.00),
('C2468135', 'SESS-2024-01', 11.75),
('A1234567', 'SESS-2024-02', 13.25),
('D1357924', 'SESS-2024-02', 17.50),
('E9876543', 'SESS-2024-01', NULL), -- Note pas encore saisie
('F3692581', 'SESS-2024-03', 15.00);
-- ── Vérification du peuplement ────────────────────────────────
SELECT 'Filiere' AS table_name, COUNT(*) AS nb FROM Filiere UNION ALL
SELECT 'Formation', COUNT(*) FROM Formation UNION ALL
SELECT 'Formateur', COUNT(*) FROM Formateur UNION ALL
SELECT 'Session', COUNT(*) FROM Session UNION ALL
SELECT 'Etudiant', COUNT(*) FROM Etudiant UNION ALL
SELECT 'Inscription', COUNT(*) FROM Inscription;
-- Quelques corrections via UPDATE pour illustrer
UPDATE Etudiant
SET telEtu = '0612000099'
WHERE numCINEtu = 'A1234567';
-- Saisir la note manquante d'E9876543
UPDATE Inscription
SET noteInscription = 12.50
WHERE numCINEtu = 'E9876543' AND codeSess = 'SESS-2024-01';
Étape 3 — Requêtes SELECT
20 requêtes SELECT progressives sur SkolaBase — filtres WHERE, tri ORDER BY, agrégats GROUP BY/HAVING, fonctions SQL (dates, chaînes, math), CASE WHEN et expressions conditionnelles.
USE skolabase;
-- Q1 : Tous les étudiants triés par nom
SELECT numCINEtu, nomEtu, prenomEtu, villeEtu FROM Etudiant ORDER BY nomEtu;
-- Q2 : Etudiants de Casablanca uniquement
SELECT * FROM Etudiant WHERE villeEtu = 'Casablanca';
-- Q3 : Etudiants dont le nom commence par 'A' ou 'B'
SELECT nomEtu, prenomEtu FROM Etudiant WHERE nomEtu LIKE 'A%' OR nomEtu LIKE 'B%';
-- Q4 : Formations de plus de 80 heures
SELECT titreForm, dureeForm, niveauForm FROM Formation WHERE dureeForm > 80 ORDER BY dureeForm DESC;
-- Q5 : Sessions en cours en 2024
SELECT codeSess, dateDebutSess, dateFinSess FROM Session
WHERE YEAR(dateDebutSess) = 2024 ORDER BY dateDebutSess;
-- Q6 : Notes entre 12 et 16 (inclus)
SELECT numCINEtu, codeSess, noteInscription FROM Inscription
WHERE noteInscription BETWEEN 12 AND 16 ORDER BY noteInscription DESC;
-- Q7 : Inscriptions sans note
SELECT numCINEtu, codeSess FROM Inscription WHERE noteInscription IS NULL;
-- Q8 : Nombre d'étudiants par ville
SELECT villeEtu, COUNT(*) AS nb_etudiants FROM Etudiant GROUP BY villeEtu ORDER BY nb_etudiants DESC;
-- Q9 : Moyenne, min et max des notes par session
SELECT
codeSess,
COUNT(*) AS nb_inscrits,
COUNT(noteInscription) AS notes_saisies,
ROUND(AVG(noteInscription), 2) AS moyenne,
MIN(noteInscription) AS note_min,
MAX(noteInscription) AS note_max
FROM Inscription GROUP BY codeSess;
-- Q10 : Sessions avec moyenne supérieure à 13
SELECT codeSess, ROUND(AVG(noteInscription), 2) AS moyenne
FROM Inscription WHERE noteInscription IS NOT NULL
GROUP BY codeSess HAVING AVG(noteInscription) > 13;
-- Q11 : Âge de chaque étudiant (calculé depuis dateNaissEtu)
SELECT
CONCAT(prenomEtu, ' ', nomEtu) AS nom_complet,
dateNaissEtu,
TIMESTAMPDIFF(YEAR, dateNaissEtu, CURDATE()) AS age
FROM Etudiant WHERE dateNaissEtu IS NOT NULL ORDER BY age;
-- Q12 : Durée de chaque session en jours
SELECT codeSess, dateDebutSess, dateFinSess,
DATEDIFF(dateFinSess, dateDebutSess) AS duree_jours
FROM Session WHERE dateFinSess IS NOT NULL;
-- Q13 : Mention selon la note (CASE WHEN)
SELECT numCINEtu, codeSess, noteInscription,
CASE
WHEN noteInscription IS NULL THEN 'Non evalue'
WHEN noteInscription >= 16 THEN 'Tres bien'
WHEN noteInscription >= 14 THEN 'Bien'
WHEN noteInscription >= 12 THEN 'Assez bien'
WHEN noteInscription >= 10 THEN 'Passable'
ELSE 'Insuffisant'
END AS mention
FROM Inscription ORDER BY noteInscription DESC;
-- Q14 : Taux de réussite par session (note >= 10)
SELECT codeSess,
COUNT(noteInscription) AS evaluations,
SUM(IF(noteInscription >= 10, 1, 0)) AS admis,
ROUND(100 * SUM(IF(noteInscription >= 10, 1, 0)) / COUNT(noteInscription), 1) AS taux_pct
FROM Inscription WHERE noteInscription IS NOT NULL GROUP BY codeSess;
-- Q15 : Formations par filière (nombre et total heures)
SELECT codeFiliere, COUNT(*) AS nb_formations, SUM(dureeForm) AS heures_totales
FROM Formation GROUP BY codeFiliere HAVING COUNT(*) >= 2;
-- Q16 : Formateur le plus actif (plus de sessions)
SELECT numFormateur, COUNT(*) AS nb_sessions FROM Session
GROUP BY numFormateur ORDER BY nb_sessions DESC LIMIT 1;
-- Q17 : Etudiants inscrits à plus d'une session
SELECT numCINEtu, COUNT(*) AS nb_sessions FROM Inscription
GROUP BY numCINEtu HAVING COUNT(*) > 1;
-- Q18 : Note de chaque étudiant avec son écart à la moyenne générale
SELECT numCINEtu, codeSess, noteInscription,
ROUND(noteInscription - (SELECT AVG(noteInscription) FROM Inscription
WHERE noteInscription IS NOT NULL), 2) AS ecart_moyenne
FROM Inscription WHERE noteInscription IS NOT NULL;
-- Q19 : Formations sans session planifiée
SELECT codeForm, titreForm FROM Formation
WHERE codeForm NOT IN (SELECT DISTINCT codeForm FROM Session);
-- Q20 : Statistiques globales SkolaBase
SELECT
(SELECT COUNT(*) FROM Etudiant) AS nb_etudiants,
(SELECT COUNT(*) FROM Session) AS nb_sessions,
(SELECT COUNT(*) FROM Inscription) AS nb_inscriptions,
(SELECT ROUND(AVG(noteInscription), 2)
FROM Inscription
WHERE noteInscription IS NOT NULL) AS moyenne_generale;
Étape 4 — Jointures & Sous-requêtes
15 requêtes avancées sur SkolaBase — jointures multi-tables (INNER, LEFT, SELF), sous-requêtes mono-ligne, IN/NOT IN, EXISTS/NOT EXISTS et sous-requêtes corrélées.
USE skolabase;
-- ── JOINTURES ────────────────────────────────────────────
-- J1 : Parcours complet (étudiant + formation + formateur)
SELECT
CONCAT(e.prenomEtu, ' ', e.nomEtu) AS etudiant,
fo.titreForm AS formation,
fi.libelleFiliere AS filiere,
CONCAT(fmt.prenomFormateur, ' ', fmt.nomFormateur) AS formateur,
s.dateDebutSess,
i.noteInscription
FROM Etudiant e
JOIN Inscription i ON e.numCINEtu = i.numCINEtu
JOIN Session s ON i.codeSess = s.codeSess
JOIN Formation fo ON s.codeForm = fo.codeForm
JOIN Filiere fi ON fo.codeFiliere = fi.codeFiliere
JOIN Formateur fmt ON s.numFormateur = fmt.numFormateur
ORDER BY e.nomEtu, s.dateDebutSess;
-- J2 : Toutes les formations avec nombre de sessions (LEFT JOIN)
SELECT fo.codeForm, fo.titreForm, fo.dureeForm,
COUNT(s.codeSess) AS nb_sessions
FROM Formation fo
LEFT JOIN Session s ON fo.codeForm = s.codeForm
GROUP BY fo.codeForm, fo.titreForm, fo.dureeForm
ORDER BY nb_sessions DESC;
-- J3 : Filières avec nombre de formations et heures totales
SELECT fi.libelleFiliere,
COUNT(fo.codeForm) AS nb_formations,
COALESCE(SUM(fo.dureeForm), 0) AS heures_totales
FROM Filiere fi
LEFT JOIN Formation fo ON fi.codeFiliere = fo.codeFiliere
GROUP BY fi.codeFiliere, fi.libelleFiliere
ORDER BY heures_totales DESC;
-- J4 : Etudiants avec leurs meilleures notes
SELECT e.numCINEtu,
CONCAT(e.prenomEtu, ' ', e.nomEtu) AS etudiant,
MAX(i.noteInscription) AS meilleure_note,
COUNT(i.codeSess) AS nb_inscriptions
FROM Etudiant e
LEFT JOIN Inscription i ON e.numCINEtu = i.numCINEtu
GROUP BY e.numCINEtu, e.nomEtu, e.prenomEtu
ORDER BY meilleure_note DESC;
-- J5 : Formateurs avec leurs sessions et formations
SELECT fmt.nomFormateur, fmt.prenomFormateur,
s.codeSess, fo.titreForm, s.dateDebutSess
FROM Formateur fmt
LEFT JOIN Session s ON fmt.numFormateur = s.numFormateur
LEFT JOIN Formation fo ON s.codeForm = fo.codeForm
ORDER BY fmt.nomFormateur, s.dateDebutSess;
-- J6 : Pairs d'étudiants dans la même session (SELF JOIN)
SELECT i1.numCINEtu AS etudiant1, i2.numCINEtu AS etudiant2, i1.codeSess
FROM Inscription i1
JOIN Inscription i2 ON i1.codeSess = i2.codeSess
AND i1.numCINEtu < i2.numCINEtu
ORDER BY i1.codeSess;
-- ── SOUS-REQUETES ────────────────────────────────────────
-- S1 : Etudiants avec note > moyenne générale
SELECT numCINEtu, codeSess, noteInscription
FROM Inscription
WHERE noteInscription > (SELECT AVG(noteInscription) FROM Inscription
WHERE noteInscription IS NOT NULL);
-- S2 : Meilleur étudiant de la session SESS-2024-01
SELECT e.numCINEtu, CONCAT(e.prenomEtu, ' ', e.nomEtu) AS etudiant, i.noteInscription
FROM Etudiant e JOIN Inscription i ON e.numCINEtu = i.numCINEtu
WHERE i.codeSess = 'SESS-2024-01'
AND i.noteInscription = (SELECT MAX(noteInscription) FROM Inscription
WHERE codeSess = 'SESS-2024-01');
-- S3 : Etudiants inscrits à au moins une session (EXISTS)
SELECT numCINEtu, nomEtu, prenomEtu FROM Etudiant e
WHERE EXISTS (SELECT 1 FROM Inscription i WHERE i.numCINEtu = e.numCINEtu);
-- S4 : Etudiants sans aucune inscription (NOT EXISTS)
SELECT numCINEtu, nomEtu, prenomEtu FROM Etudiant e
WHERE NOT EXISTS (SELECT 1 FROM Inscription i WHERE i.numCINEtu = e.numCINEtu);
-- S5 : Formations qui ont eu au moins une session en 2024
SELECT codeForm, titreForm FROM Formation
WHERE codeForm IN (SELECT codeForm FROM Session WHERE YEAR(dateDebutSess) = 2024);
-- S6 : Formateur avec le plus de sessions animées
SELECT numFormateur, nomFormateur, prenomFormateur FROM Formateur
WHERE numFormateur = (SELECT numFormateur FROM Session
GROUP BY numFormateur ORDER BY COUNT(*) DESC LIMIT 1);
-- S7 : Etudiants dont la note dans SESS-2024-01 est > leur propre moyenne
SELECT i1.numCINEtu, i1.noteInscription AS note_sess01
FROM Inscription i1
WHERE i1.codeSess = 'SESS-2024-01'
AND i1.noteInscription > (SELECT AVG(i2.noteInscription)
FROM Inscription i2
WHERE i2.numCINEtu = i1.numCINEtu
AND i2.noteInscription IS NOT NULL);
-- S8 : Sessions de la formation la plus longue
SELECT codeSess, dateDebutSess FROM Session
WHERE codeForm = (SELECT codeForm FROM Formation ORDER BY dureeForm DESC LIMIT 1);
-- S9 : Table dérivée : top 3 sessions par moyenne
SELECT s.codeSess, s.dateDebutSess, stats.moyenne
FROM Session s
JOIN (SELECT codeSess, ROUND(AVG(noteInscription), 2) AS moyenne
FROM Inscription WHERE noteInscription IS NOT NULL
GROUP BY codeSess) AS stats ON s.codeSess = stats.codeSess
ORDER BY stats.moyenne DESC LIMIT 3;
Étape 5 — Administration SkolaBase
Script d'administration complet — sauvegarde avec mysqldump, création des 4 comptes utilisateurs selon le principe du moindre privilège, vérification des droits et procédure de restauration.
# Sauvegarde complète (structure + données)
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
skolabase > skolabase_backup_$(date +%Y%m%d).sql
# Vérifier le fichier généré
ls -lh skolabase_backup_*.sql
# skolabase_backup_20241115.sql 12K
# Sauvegarde structure seulement (partager le schéma)
mysqldump -u root -p --no-data skolabase > skolabase_schema.sql
# Restauration complète
mysql -u root -p -e "DROP DATABASE IF EXISTS skolabase;"
mysql -u root -p -e "CREATE DATABASE skolabase CHARACTER SET utf8mb4;"
mysql -u root -p skolabase < skolabase_backup_20241115.sql
# Vérification après restauration
mysql -u root -p skolabase -e "SHOW TABLES; SELECT COUNT(*) FROM Etudiant;"
-- ── Création des 4 comptes ──────────────────────────────────
-- Compte administrateur (droits complets sur skolabase)
CREATE USER IF NOT EXISTS 'skola_admin'@'localhost'
IDENTIFIED BY 'SkolaAdmin_2024!';
GRANT ALL PRIVILEGES ON skolabase.* TO 'skola_admin'@'localhost';
-- Compte application web (CRUD sans DROP/CREATE/ALTER)
CREATE USER IF NOT EXISTS 'skola_app'@'localhost'
IDENTIFIED BY 'SkolaApp_2024!';
GRANT SELECT, INSERT, UPDATE, DELETE ON skolabase.*
TO 'skola_app'@'localhost';
-- Compte secrétariat (Etudiant + Inscription uniquement)
CREATE USER IF NOT EXISTS 'secretariat'@'localhost'
IDENTIFIED BY 'Secretariat_2024!';
GRANT SELECT, INSERT, UPDATE ON skolabase.Etudiant
TO 'secretariat'@'localhost';
GRANT SELECT, INSERT ON skolabase.Inscription
TO 'secretariat'@'localhost';
GRANT SELECT ON skolabase.Session
TO 'secretariat'@'localhost';
-- Compte rapports (lecture seule depuis n'importe où)
CREATE USER IF NOT EXISTS 'skola_reader'@'%'
IDENTIFIED BY 'SkolaReader_2024!';
GRANT SELECT ON skolabase.* TO 'skola_reader'@'%';
-- ── Vérification ───────────────────────────────────────────
SHOW GRANTS FOR 'skola_app'@'localhost';
SHOW GRANTS FOR 'secretariat'@'localhost';
SELECT User, Host, plugin
FROM mysql.user WHERE User LIKE 'skola%' OR User = 'secretariat';
-- ── Tests de connexion ───────────────────────────────────────
-- (exécuter depuis le terminal)
-- mysql -u skola_app -p skolabase -e "SELECT COUNT(*) FROM Etudiant;"
-- mysql -u skola_reader -p skolabase -e "SELECT titreForm FROM Formation;"
-- mysql -u secretariat -p skolabase -e "DROP TABLE Etudiant;" -- doit échouer !
-- ── Maintenance ─────────────────────────────────────────────
-- Changer le mot de passe d'un compte
ALTER USER 'skola_app'@'localhost'
IDENTIFIED BY 'NouveauMdp_SkolaApp_2025!';
-- Révoquer le DELETE pour skola_app (restriction de sécurité)
REVOKE DELETE ON skolabase.* FROM 'skola_app'@'localhost';
SHOW GRANTS FOR 'skola_app'@'localhost';
-- Supprimer un compte obsolète
DROP USER IF EXISTS 'skola_reader'@'%';
-- ── Bilan de sécurité SkolaBase ──────────────────────────────
SELECT
TABLE_SCHEMA AS base,
GRANTEE AS utilisateur,
PRIVILEGE_TYPE AS privilege,
IS_GRANTABLE AS peut_transmettre
FROM information_schema.SCHEMA_PRIVILEGES
WHERE TABLE_SCHEMA = 'skolabase'
ORDER BY GRANTEE, PRIVILEGE_TYPE;
| Étape | Compétences | Parties du cours |
|---|---|---|
| CDC & Merise | RG, DD, DF, MCD, MLD | Parties 1 & 2 |
| Workbench | EER, Forward Engineering, FK graphiques | Partie 3 |
| DDL | CREATE DATABASE, CREATE TABLE, contraintes nommées | Parties 4 & 5 |
| DML | INSERT multi-lignes, UPDATE, DELETE, TRUNCATE | Partie 6 |
| SELECT | WHERE, GROUP BY, HAVING, fonctions, CASE WHEN | Partie 7 |
| Requêtes avancées | JOIN 5 tables, sous-requêtes, EXISTS, corrélées | Partie 8 |
| Administration | mysqldump, CREATE USER, GRANT, REVOKE | Partie 9 |