Apprendre MySQL
De débutant à expert — 50 leçons progressives et détaillées
Introduction à MySQL
MySQL est l'un des systèmes de gestion de bases de données relationnelles (SGBDR) les plus populaires au monde. Créé en 1995 par Michael Widenius et David Axmark en Suède, puis racheté par Sun Microsystems en 2008 et aujourd'hui maintenu par Oracle, il est utilisé par des millions d'applications allant de simples blogs à de gigantesques plateformes technologiques.
Pourquoi une base de données ? Une base de données permet de stocker des informations de manière organisée, persistante et efficace. Contrairement à un simple fichier CSV ou texte, elle permet de chercher, trier, relier et sécuriser des données en quelques millisecondes même avec des millions d'enregistrements.
MySQL s'intègre avec presque tous les langages modernes :
| Langage | Framework populaire | Connecteur |
|---|---|---|
| PHP | Laravel, Symfony | PDO, mysqli |
| Python | Django, Flask | mysql-connector, SQLAlchemy |
| JavaScript | Node.js, Express | mysql2, Sequelize |
| Java | Spring Boot | JDBC, Hibernate |
| Go | Gin, Echo | go-sql-driver/mysql |
| Ruby | Ruby on Rails | mysql2 gem |
L'architecture de MySQL repose sur un modèle client-serveur : le serveur MySQL gère les données, et les clients (applications, phpMyAdmin, terminal) envoient des requêtes SQL pour interagir avec ces données.
Les données dans MySQL sont persistantes : elles survivent aux redémarrages serveur. Elles sont aussi relationnelles : les tables peuvent être liées entre elles via des clés, évitant la duplication d'informations.
Installation de MySQL
Pour démarrer, la méthode la plus simple est d'installer une suite logicielle tout-en-un. Ces packs installent en une seule opération : Apache (serveur web), PHP, MySQL et phpMyAdmin.
| Suite | Systèmes | Idéal pour |
|---|---|---|
| XAMPP | Windows, macOS, Linux | Tous niveaux |
| WAMP | Windows | Développeurs Windows |
| MAMP | macOS, Windows | Utilisateurs Mac |
| Laragon | Windows | Laravel/PHP |
Pour une installation directe en ligne de commande (Linux/Mac) :
# Ubuntu / Debian
sudo apt update
sudo apt install mysql-server
# macOS via Homebrew
brew install mysql
# Démarrer le service MySQL
sudo systemctl start mysql
# Sécuriser l'installation (recommandé)
sudo mysql_secure_installation
En production : installez MySQL directement sur le serveur Linux et configurez un pare-feu. XAMPP/WAMP sont destinés uniquement au développement local. Ne jamais exposer le port 3306 sur Internet sans sécurisation.
Comprendre phpMyAdmin
phpMyAdmin est une interface graphique web pour gérer MySQL sans écrire toutes les commandes à la main. C'est l'outil idéal pour débuter et visualiser vos données.
http://localhost/phpmyadmin
Depuis phpMyAdmin :
- ✏️ Créer et supprimer des bases de données
- 📋 Créer et modifier des tables
- ➕ Ajouter, modifier et supprimer des données
- 🔍 Exécuter des requêtes SQL directement
- 💾 Exporter / importer des bases (sauvegardes .sql)
- 👤 Gérer les utilisateurs et leurs droits
- 📊 Voir les statistiques des tables et index
L'onglet "SQL" de phpMyAdmin vous permet d'écrire et tester vos requêtes. C'est votre bac à sable pour apprendre ! Vous pouvez aussi utiliser MySQL Workbench (outil officiel Oracle) ou DBeaver (open-source très complet) comme alternatives.
✅ phpMyAdmin
- Accessible via navigateur
- Inclus dans XAMPP/WAMP
- Interface simple
✅ MySQL Workbench
- Outil officiel Oracle
- Modélisation visuelle des tables
- Analyse des requêtes
Créer une base de données
Une base de données est un conteneur logique regroupant plusieurs tables. Pensez-y comme un classeur : le classeur = la base de données, les onglets = les tables.
CREATE DATABASE nom_de_la_base [CHARACTER SET utf8mb4] [COLLATE utf8mb4_unicode_ci];
CREATE DATABASE boutique;
-- Avec encodage recommandé (accents, emojis, toutes les langues)
CREATE DATABASE boutique
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- Lister toutes les bases existantes
SHOW DATABASES;
-- Supprimer une base (⚠️ irréversible)
DROP DATABASE boutique;
-- Créer seulement si elle n'existe pas déjà
CREATE DATABASE IF NOT EXISTS boutique;
Bonne pratique : toujours créer vos bases en utf8mb4 avec la collation utf8mb4_unicode_ci. Cela supporte tous les caractères : accents français, caractères arabes, emojis 😊, alphabets asiatiques…
Sélectionner une base de données
Après avoir créé une base, vous devez indiquer à MySQL que vous souhaitez travailler dedans avec la commande USE.
USE boutique;
-- Vérifier quelle base est active
SELECT DATABASE(); -- retourne "boutique"
-- Lister les tables de la base active
SHOW TABLES;
-- Voir la structure complète de la base
SHOW TABLE STATUS;
Si vous oubliez le USE, MySQL retournera l'erreur "No database selected". Dans phpMyAdmin, cliquer sur une base dans le panneau gauche fait automatiquement le USE.
Comprendre les Tables
Une table est la structure fondamentale de stockage. Elle s'organise comme un tableau avec des colonnes (les attributs) et des lignes (les enregistrements, aussi appelés tuples).
| ID | Nom | Pays | Actif | |
|---|---|---|---|---|
| 1 | Aliou Diallo | aliou@gmail.com | Sénégal | ✅ |
| 2 | Fatoumata Bah | fato@outlook.com | Guinée | ✅ |
| 3 | Moussa Koné | moussa@yahoo.fr | Mali | ❌ |
Concepts essentiels :
- Colonne (champ) : définit un attribut de l'entité (nom, email, âge…)
- Ligne (enregistrement) : représente une instance unique de l'entité
- Cellule : intersection d'une colonne et d'une ligne = une valeur précise
- Schéma : la structure d'une table (colonnes + types + contraintes)
Une base bien conçue décompose les informations en plusieurs tables liées. Par exemple : utilisateurs, commandes, produits. Cela évite la duplication et garantit la cohérence — c'est le principe de la normalisation.
Créer une Table — CREATE TABLE
La commande CREATE TABLE définit la structure d'une table : ses colonnes, leurs types et leurs contraintes.
CREATE TABLE nom_table (col1 TYPE [contraintes], col2 TYPE [contraintes], ...);
CREATE TABLE utilisateurs (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
age INT
);
-- Voir les tables de la base
SHOW TABLES;
-- Voir la structure détaillée d'une table
DESCRIBE utilisateurs;
-- ou (équivalent)
SHOW COLUMNS FROM utilisateurs;
-- Voir le SQL complet de création
SHOW CREATE TABLE utilisateurs;
Toujours inclure une colonne id INT AUTO_INCREMENT PRIMARY KEY comme première colonne. C'est la clé primaire qui identifie chaque ligne de manière unique et sert de référence dans les jointures.
Les Types de Données
Chaque colonne doit avoir un type précis. Choisir le bon type est essentiel pour les performances, l'espace disque et la cohérence des données.
Types numériques :
| Type | Plage | Espace | Usage |
|---|---|---|---|
| TINYINT | -128 à 127 | 1 octet | Booléen, petits entiers |
| INT | -2M à +2M | 4 octets | IDs, âges, compteurs |
| BIGINT | ±9 milliards² | 8 octets | Grands IDs, montants |
| DECIMAL(10,2) | précis | variable | Prix, salaires |
| FLOAT | approximatif | 4 octets | Notes, températures |
Types texte :
| Type | Longueur max | Usage |
|---|---|---|
| CHAR(n) | 255 car. | Longueur fixe (codes postaux) |
| VARCHAR(n) | 65 535 car. | Noms, emails, titres |
| TEXT | 65 535 car. | Articles, descriptions |
| MEDIUMTEXT | 16 Mo | Longs contenus HTML |
| LONGTEXT | 4 Go | Très longs textes |
| ENUM | 65535 valeurs | statut ('actif','inactif') |
Types dates :
| Type | Format | Usage |
|---|---|---|
| DATE | AAAA-MM-JJ | Date de naissance |
| TIME | HH:MM:SS | Heure uniquement |
| DATETIME | AAAA-MM-JJ HH:MM:SS | Timestamp de création |
| TIMESTAMP | AAAA-MM-JJ HH:MM:SS | Mise à jour auto (ON UPDATE) |
| YEAR | AAAA | Année de publication |
Pour les montants financiers, utilisez toujours DECIMAL(10,2) jamais FLOAT. Le type FLOAT est approximatif : 0.1 + 0.2 = 0.30000000000000004 — catastrophique pour les calculs d'argent !
Insérer des Données — INSERT INTO
INSERT INTO ajoute de nouvelles lignes dans une table. C'est le moyen de peupler votre base.
INSERT INTO table (col1, col2) VALUES (val1, val2);
-- Insertion simple
INSERT INTO utilisateurs (nom, email, age)
VALUES ('Aliou Diallo', 'aliou@gmail.com', 25);
-- Insertion multiple en une requête (bien plus performant)
INSERT INTO utilisateurs (nom, email, age)
VALUES
('Fatoumata Bah', 'fato@gmail.com', 30),
('Moussa Koné', 'moussa@gmail.com', 28),
('Aminata Sow', 'ami@gmail.com', 22);
-- Insérer et récupérer l'ID généré
INSERT INTO utilisateurs (nom, email) VALUES ('Awa', 'awa@test.com');
SELECT LAST_INSERT_ID(); -- retourne l'ID de la ligne insérée
-- Insérer OU mettre à jour si l'email existe déjà (UPSERT)
INSERT INTO utilisateurs (nom, email) VALUES ('Ali', 'ali@test.com')
ON DUPLICATE KEY UPDATE nom = 'Ali (mis à jour)';
Insérer plusieurs lignes en une seule requête est 3 à 10 fois plus rapide que de faire des INSERT séparés, car MySQL n'ouvre qu'une seule connexion et transaction.
Lire les Données — SELECT
SELECT est la commande la plus utilisée en SQL. Elle récupère des données d'une ou plusieurs tables sans les modifier.
-- Toutes les colonnes
SELECT * FROM utilisateurs;
-- Colonnes spécifiques (recommandé en production)
SELECT nom, email FROM utilisateurs;
-- Alias de colonnes
SELECT nom AS 'Prénom', email AS 'Adresse Email'
FROM utilisateurs;
-- Valeur calculée dans le SELECT
SELECT nom,
prix * 1.2 AS prix_ttc
FROM produits;
-- Éliminer les doublons avec DISTINCT
SELECT DISTINCT pays FROM utilisateurs;
❌ À éviter
SELECT * en production : retourne toutes les colonnes, surcharge le réseau, révèle la structure de vos tables, incompatible avec les index couvrants.
✅ Recommandé
Nommez explicitement les colonnes : SELECT id, nom, email. Plus rapide, plus lisible, plus sécurisé, permet les optimisations d'index.
Quelle requête retourne uniquement les noms de tous les pays présents dans la table utilisateurs, sans doublons ?
Filtrer avec WHERE
La clause WHERE filtre les lignes retournées. Sans elle, MySQL traite toutes les lignes — très coûteux sur de grandes tables.
-- Condition simple
SELECT * FROM utilisateurs WHERE id = 1;
-- Combinaison AND / OR / NOT
SELECT * FROM utilisateurs
WHERE pays = 'Sénégal' AND age > 20;
SELECT * FROM utilisateurs
WHERE pays = 'Mali' OR pays = 'Guinée';
SELECT * FROM utilisateurs
WHERE NOT pays = 'Sénégal';
-- Parenthèses pour grouper les conditions
SELECT * FROM commandes
WHERE (statut = 'en_attente' OR statut = 'expédié')
AND montant > 10000;
| Opérateur | Signification | Exemple |
|---|---|---|
| = | Égal | WHERE age = 25 |
| != / <> | Différent | WHERE pays != 'Mali' |
| > / < | Supérieur / Inférieur | WHERE prix > 500 |
| >= / <= | Sup. ou égal / Inf. ou égal | WHERE note >= 10 |
| IS NULL | Valeur vide | WHERE email IS NULL |
| IS NOT NULL | Valeur non vide | WHERE email IS NOT NULL |
Pour comparer avec NULL, n'utilisez jamais = NULL mais toujours IS NULL. En SQL, NULL = NULL retourne FALSE — car NULL signifie "valeur inconnue".
Trier les résultats — ORDER BY
Sans ORDER BY, MySQL ne garantit aucun ordre — il retourne les lignes dans l'ordre le plus pratique pour lui. Utilisez toujours ORDER BY quand l'ordre compte.
-- Tri alphabétique A→Z (ASC = défaut)
SELECT * FROM utilisateurs ORDER BY nom ASC;
-- Tri du plus récent au plus ancien
SELECT * FROM commandes ORDER BY date_commande DESC;
-- Tri multi-colonnes : par catégorie ASC, puis prix DESC
SELECT * FROM produits
ORDER BY categorie ASC, prix DESC;
-- Tri par numéro de colonne (position dans SELECT)
SELECT nom, age FROM utilisateurs ORDER BY 2 DESC; -- tri par 'age'
-- NULL en dernier (comportement par défaut avec ASC)
SELECT * FROM produits ORDER BY note IS NULL ASC, note DESC;
ASC = Ascendant (A→Z, 0→9, date ancienne→récente).
DESC = Descendant (Z→A, 9→0, date récente→ancienne).
Par défaut, les valeurs NULL apparaissent en premier avec ASC et en dernier avec DESC.
Limiter les résultats — LIMIT
LIMIT est indispensable pour la pagination et les performances. Il évite de ramener des millions de lignes inutilement.
-- Les 5 premiers
SELECT * FROM utilisateurs LIMIT 5;
-- LIMIT offset, count (page 2 = sauter 10, prendre 10)
SELECT * FROM utilisateurs LIMIT 10, 10;
-- Syntaxe alternative (plus lisible)
SELECT * FROM utilisateurs LIMIT 10 OFFSET 20; -- page 3
-- Toujours avec ORDER BY pour une pagination cohérente
SELECT * FROM articles
ORDER BY date_publication DESC
LIMIT 0, 10; -- Page 1
Formule pagination : LIMIT (page-1)*par_page, par_page
Page 1 avec 10/page → LIMIT 0, 10
Page 3 avec 10/page → LIMIT 20, 10
Page 5 avec 25/page → LIMIT 100, 25
Modifier des données — UPDATE
UPDATE modifie les valeurs de lignes existantes dans une table.
-- Modifier un seul enregistrement
UPDATE utilisateurs
SET nom = 'Mamadou Diallo'
WHERE id = 1;
-- Modifier plusieurs colonnes à la fois
UPDATE utilisateurs
SET nom = 'Mamadou', email = 'mamadou@gmail.com'
WHERE id = 1;
-- Incrémenter une valeur (ex: ajouter des points)
UPDATE utilisateurs
SET points = points + 100
WHERE id = 1;
-- Vérifier combien de lignes seraient affectées avant de modifier
SELECT COUNT(*) FROM utilisateurs WHERE actif = 0;
DANGER : Ne JAMAIS oublier WHERE !
UPDATE utilisateurs SET nom = 'Test';
Sans WHERE → tous les utilisateurs sont renommés "Test". Il n'y a pas d'annulation automatique (sauf dans une transaction).
Supprimer des données — DELETE
DELETE supprime des lignes d'une table. Action irréversible sans transaction ou sauvegarde.
-- Supprimer un enregistrement précis
DELETE FROM utilisateurs WHERE id = 5;
-- Supprimer selon une condition
DELETE FROM utilisateurs WHERE actif = 0;
-- DELETE avec ORDER BY et LIMIT (ex: supprimer les 100 plus anciens)
DELETE FROM logs ORDER BY date_log ASC LIMIT 100;
❌ DELETE FROM table;
Supprime toutes les lignes, ligne par ligne, avec log. Très lent sur grandes tables. Laisse l'AUTO_INCREMENT intact.
✅ TRUNCATE TABLE table;
Vide toute la table instantanément. Réinitialise l'AUTO_INCREMENT. Bien plus rapide. Utilisez-le pour vider une table en dev.
Clés Primaires — PRIMARY KEY
Une clé primaire identifie de manière unique chaque ligne d'une table. Elle garantit l'absence de doublons et sert de référence dans les jointures.
-- Clé primaire simple
CREATE TABLE utilisateurs (
id INT PRIMARY KEY,
nom VARCHAR(100)
);
-- Clé primaire composite (sur plusieurs colonnes)
CREATE TABLE inscriptions (
etudiant_id INT,
cours_id INT,
PRIMARY KEY (etudiant_id, cours_id)
);
-- Tentative de doublon → erreur
INSERT INTO utilisateurs VALUES (1, 'Ali');
INSERT INTO utilisateurs VALUES (1, 'Moussa'); -- ❌ Duplicate entry '1'
La clé primaire crée automatiquement un index sur la colonne. C'est pourquoi les recherches par ID (WHERE id = 5) sont instantanées même sur des tables de millions de lignes.
AUTO_INCREMENT
AUTO_INCREMENT génère automatiquement des identifiants uniques et croissants. Vous n'avez plus à gérer les IDs manuellement.
CREATE TABLE utilisateurs (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100)
);
INSERT INTO utilisateurs (nom) VALUES ('Ali'); -- id = 1
INSERT INTO utilisateurs (nom) VALUES ('Fato'); -- id = 2
INSERT INTO utilisateurs (nom) VALUES ('Moussa'); -- id = 3
-- Si on supprime id=3 et en insère un nouveau → id = 4 (jamais réutilisé)
-- Définir la valeur de départ
ALTER TABLE utilisateurs AUTO_INCREMENT = 1000;
-- Voir la prochaine valeur AUTO_INCREMENT
SHOW TABLE STATUS LIKE 'utilisateurs';
Un ID supprimé n'est jamais réutilisé. Si vous supprimez les IDs 98, 99, 100 et insérez un nouveau, il recevra l'ID 101. C'est un comportement intentionnel pour éviter les conflits.
NOT NULL, UNIQUE et DEFAULT
Ces contraintes garantissent la qualité et la cohérence des données insérées dans votre base.
| Contrainte | Rôle | Erreur si violée |
|---|---|---|
| NOT NULL | Valeur obligatoire | "Column cannot be null" |
| UNIQUE | Valeur unique dans la colonne | "Duplicate entry" |
| DEFAULT val | Valeur si rien n'est fourni | — |
| CHECK (condition) | Valide une règle métier | "Check constraint violated" |
CREATE TABLE utilisateurs (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
pays VARCHAR(50) DEFAULT 'Sénégal',
actif BOOLEAN DEFAULT 1,
age INT CHECK (age >= 0 AND age <= 150),
créé_le TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Clés Étrangères — FOREIGN KEY
Une clé étrangère est une colonne qui référence la clé primaire d'une autre table. Elle maintient l'intégrité référentielle : on ne peut pas créer une commande pour un utilisateur qui n'existe pas.
CREATE TABLE commandes (
id INT AUTO_INCREMENT PRIMARY KEY,
utilisateur_id INT NOT NULL,
produit VARCHAR(200),
montant DECIMAL(10,2),
FOREIGN KEY (utilisateur_id)
REFERENCES utilisateurs(id)
ON DELETE CASCADE -- supprime les commandes si l'utilisateur est supprimé
ON UPDATE CASCADE -- met à jour si l'ID change
);
| Option | Comportement |
|---|---|
| ON DELETE CASCADE | Supprime les lignes liées automatiquement |
| ON DELETE SET NULL | Met la FK à NULL (colonne doit accepter NULL) |
| ON DELETE RESTRICT | Interdit la suppression si des lignes liées existent |
| ON DELETE NO ACTION | Comme RESTRICT (comportement par défaut) |
Modifier une Table — ALTER TABLE
ALTER TABLE modifie la structure d'une table existante sans perdre les données.
-- Ajouter une colonne
ALTER TABLE utilisateurs
ADD COLUMN telephone VARCHAR(20);
-- Ajouter une colonne à une position précise
ALTER TABLE utilisateurs
ADD COLUMN telephone VARCHAR(20) AFTER email;
-- Modifier le type d'une colonne
ALTER TABLE utilisateurs
MODIFY COLUMN nom VARCHAR(200) NOT NULL;
-- Renommer une colonne
ALTER TABLE utilisateurs
RENAME COLUMN nom TO nom_complet;
-- Supprimer une colonne
ALTER TABLE utilisateurs
DROP COLUMN telephone;
-- Renommer la table
RENAME TABLE utilisateurs TO membres;
Sur de très grandes tables (millions de lignes), un ALTER TABLE peut prendre plusieurs minutes et bloquer les écritures. Utilisez des outils comme gh-ost ou pt-online-schema-change en production.
Introduction aux Jointures (JOIN)
Dans une base bien conçue, les données sont réparties en plusieurs tables. Les jointures les combinent dans une seule requête en s'appuyant sur les clés.
❌ Sans jointures
- Données dupliquées
- Mises à jour difficiles
- Incohérences possibles
✅ Avec jointures
- Données normalisées
- Un seul endroit à mettre à jour
- Cohérence garantie
| Type de JOIN | Lignes retournées |
|---|---|
| INNER JOIN | Seulement les lignes avec correspondance dans les deux tables |
| LEFT JOIN | Toutes les lignes de gauche + correspondances droite (NULL si absent) |
| RIGHT JOIN | Toutes les lignes de droite + correspondances gauche (NULL si absent) |
| CROSS JOIN | Produit cartésien — toutes les combinaisons possibles |
| SELF JOIN | Une table jointe avec elle-même |
INNER JOIN
INNER JOIN retourne uniquement les lignes ayant une correspondance dans les deux tables. C'est le JOIN le plus courant.
-- Commandes avec les infos de l'utilisateur
SELECT u.nom, c.produit, c.montant
FROM utilisateurs AS u
INNER JOIN commandes AS c
ON u.id = c.utilisateur_id;
-- JOIN sur 3 tables
SELECT u.nom, c.produit, p.categorie
FROM utilisateurs u
INNER JOIN commandes c ON u.id = c.utilisateur_id
INNER JOIN produits p ON c.produit_id = p.id;
Utilisez des alias courts (u, c, p) pour les tables dans les JOIN. Cela rend les requêtes bien plus lisibles. Qualifiez toujours les colonnes ambiguës : u.id et non juste id.
LEFT JOIN et RIGHT JOIN
LEFT JOIN retourne toutes les lignes de la table gauche, même si aucune correspondance n'existe à droite (les colonnes absentes valent NULL).
-- Tous les utilisateurs, même ceux sans commande
SELECT u.nom, c.produit
FROM utilisateurs u
LEFT JOIN commandes c
ON u.id = c.utilisateur_id;
-- c.produit vaut NULL pour les utilisateurs sans commande
-- Trouver les utilisateurs qui n'ont JAMAIS commandé
SELECT u.nom
FROM utilisateurs u
LEFT JOIN commandes c
ON u.id = c.utilisateur_id
WHERE c.id IS NULL; -- Technique anti-JOIN
Astuce anti-JOIN : en ajoutant WHERE c.id IS NULL après un LEFT JOIN, on trouve précisément les lignes sans correspondance — très utile pour détecter des données orphelines.
SELF JOIN & CROSS JOIN
Le SELF JOIN joint une table avec elle-même — utile pour les hiérarchies (employé/manager). Le CROSS JOIN produit toutes les combinaisons possibles.
-- Trouver le manager de chaque employé
SELECT
e.nom AS employe,
m.nom AS manager
FROM employes AS e
LEFT JOIN employes AS m
ON e.manager_id = m.id;
-- Générer toutes les combinaisons taille × couleur
SELECT t.taille, c.couleur
FROM tailles t
CROSS JOIN couleurs c;
-- Si 3 tailles × 5 couleurs = 15 lignes résultantes
GROUP BY et HAVING
GROUP BY regroupe les lignes partageant la même valeur pour appliquer des agrégats.
-- Nombre d'utilisateurs par pays
SELECT pays, COUNT(*) AS total
FROM utilisateurs
GROUP BY pays
ORDER BY total DESC;
-- HAVING : filtre appliqué APRÈS le regroupement
SELECT pays, COUNT(*) AS total
FROM utilisateurs
GROUP BY pays
HAVING total > 5
ORDER BY total DESC;
-- Chiffre d'affaires par mois
SELECT
YEAR(date_commande) AS annee,
MONTH(date_commande) AS mois,
SUM(montant) AS ca
FROM commandes
GROUP BY annee, mois
ORDER BY annee, mois;
WHERE filtre avant le regroupement (sur les lignes brutes). HAVING filtre après (sur les résultats d'agrégation). On peut utiliser les deux dans la même requête.
Fonctions sur les chaînes de caractères
MySQL offre de nombreuses fonctions pour manipuler les textes directement en SQL.
-- Concaténer des chaînes
SELECT CONCAT(prenom, ' ', nom) AS nom_complet FROM utilisateurs;
SELECT CONCAT_WS(', ', ville, pays) AS adresse FROM utilisateurs;
-- Casse
SELECT UPPER(nom), LOWER(email) FROM utilisateurs;
-- Longueur et extraction
SELECT LENGTH(nom) AS nb_chars FROM utilisateurs;
SELECT SUBSTRING(email, 1, 5) FROM utilisateurs; -- 5 premiers caractères
SELECT LEFT(nom, 3), RIGHT(nom, 3) FROM utilisateurs;
-- Nettoyage
SELECT TRIM(' Aliou '); -- "Aliou" (supprime espaces)
SELECT REPLACE(email, '@gmail.com', '@outlook.com') FROM utilisateurs;
-- Recherche de position
SELECT INSTR('bonjour@gmail.com', '@'); -- retourne 8
Fonctions d'agrégation : COUNT, SUM, AVG, MIN, MAX
Ces 5 fonctions calculent des statistiques sur un ensemble de lignes. Elles s'utilisent souvent avec GROUP BY.
SELECT COUNT(*) AS total_utilisateurs FROM utilisateurs;
SELECT COUNT(email) AS avec_email FROM utilisateurs; -- ignore NULL
SELECT COUNT(DISTINCT pays) AS nb_pays FROM utilisateurs;
SELECT SUM(montant) AS ca_total FROM commandes;
SELECT AVG(montant) AS panier_moyen FROM commandes;
SELECT MIN(prix), MAX(prix) FROM produits;
-- Tout en une requête
SELECT
COUNT(*) AS nb_commandes,
SUM(montant) AS total,
AVG(montant) AS moyenne,
MIN(montant) AS plus_petite,
MAX(montant) AS plus_grande
FROM commandes
WHERE statut = 'terminé';
COUNT(*) compte toutes les lignes y compris celles avec NULL. COUNT(colonne) ignore les valeurs NULL — la différence peut être significative !
Fonctions de dates
MySQL dispose d'un arsenal complet de fonctions pour manipuler les dates et les heures.
-- Date et heure actuelle
SELECT NOW(); -- 2026-06-08 14:30:00
SELECT CURDATE(); -- 2026-06-08
SELECT CURTIME(); -- 14:30:00
-- Extraire des parties
SELECT YEAR(NOW()), MONTH(NOW()), DAY(NOW());
SELECT HOUR(NOW()), MINUTE(NOW()), WEEKDAY(NOW());
-- Calculs de dates
SELECT DATE_ADD(NOW(), INTERVAL 30 DAY); -- dans 30 jours
SELECT DATE_SUB(NOW(), INTERVAL 1 YEAR); -- il y a 1 an
SELECT DATEDIFF('2026-12-31', CURDATE()); -- jours restants en 2026
-- Formater une date
SELECT DATE_FORMAT(NOW(), '%d/%m/%Y %H:%i'); -- "08/06/2026 14:30"
-- Commandes des 7 derniers jours
SELECT * FROM commandes
WHERE date_commande >= DATE_SUB(NOW(), INTERVAL 7 DAY);
Sous-requêtes (Subqueries)
Une sous-requête est une requête imbriquée à l'intérieur d'une autre. Elle permet des filtres dynamiques sans créer de tables temporaires.
-- Utilisateurs ayant commandé plus que la moyenne
SELECT * FROM commandes
WHERE montant > (
SELECT AVG(montant) FROM commandes
);
-- Sous-requête dans FROM (table dérivée)
SELECT stats.pays, stats.total
FROM (
SELECT pays, COUNT(*) AS total
FROM utilisateurs
GROUP BY pays
) AS stats
WHERE stats.total > 3;
-- EXISTS : vérifie l'existence d'une correspondance
SELECT * FROM utilisateurs u
WHERE EXISTS (
SELECT 1 FROM commandes c
WHERE c.utilisateur_id = u.id
);
Quand possible, préférez les JOIN aux sous-requêtes — ils sont généralement plus performants car MySQL peut les optimiser plus facilement. Mais les sous-requêtes sont parfois plus lisibles.
UNION et UNION ALL
UNION combine les résultats de plusieurs SELECT en une seule liste. Les colonnes doivent être compatibles en nombre et type.
-- UNION : élimine les doublons (plus lent)
SELECT email FROM utilisateurs
UNION
SELECT email FROM employes;
-- UNION ALL : garde les doublons (plus rapide)
SELECT nom, 'Utilisateur' AS type FROM utilisateurs
UNION ALL
SELECT nom, 'Employé' AS type FROM employes
ORDER BY nom;
Utilisez UNION ALL si vous savez qu'il n'y a pas de doublons ou si vous voulez les conserver. C'est bien plus performant que UNION qui fait un tri pour éliminer les doublons.
LIKE, IN et BETWEEN
Ces opérateurs permettent des conditions de filtrage plus expressives que les simples comparaisons.
-- LIKE : recherche par motif (% = n'importe quoi, _ = 1 caractère)
SELECT * FROM utilisateurs WHERE nom LIKE 'Ali%'; -- commence par Ali
SELECT * FROM utilisateurs WHERE nom LIKE '%ba'; -- finit par ba
SELECT * FROM utilisateurs WHERE nom LIKE '%ou%'; -- contient ou
SELECT * FROM utilisateurs WHERE nom LIKE 'A___'; -- A suivi de 3 chars
SELECT * FROM utilisateurs WHERE nom NOT LIKE '%test%'; -- exclure les tests
-- IN : liste de valeurs exactes
SELECT * FROM utilisateurs
WHERE pays IN ('Sénégal', 'Mali', 'Guinée');
-- NOT IN : exclure une liste
SELECT * FROM produits
WHERE categorie NOT IN ('archive', 'supprimé');
-- BETWEEN : plage inclusive
SELECT * FROM utilisateurs WHERE age BETWEEN 18 AND 30;
SELECT * FROM commandes
WHERE date_commande BETWEEN '2026-01-01' AND '2026-12-31';
LIKE '%mot%' (avec % au début) ne peut pas utiliser un index et force un scan complet de la table. Évitez-le sur de grandes tables. Pour la recherche full-text, utilisez FULLTEXT INDEX avec MATCH ... AGAINST.
CASE WHEN — Conditions dans les requêtes
CASE WHEN est l'équivalent SQL du if/else. Il permet de créer des colonnes calculées selon des conditions.
-- Catégoriser les utilisateurs par âge
SELECT nom, age,
CASE
WHEN age < 18 THEN 'Mineur'
WHEN age BETWEEN 18 AND 25 THEN 'Jeune adulte'
WHEN age BETWEEN 26 AND 60 THEN 'Adulte'
ELSE 'Senior'
END AS tranche_age
FROM utilisateurs;
-- Dans un GROUP BY : stats par catégorie de prix
SELECT
CASE
WHEN prix < 1000 THEN 'Entrée de gamme'
WHEN prix < 10000 THEN 'Milieu de gamme'
ELSE 'Haut de gamme'
END AS gamme,
COUNT(*) AS nb_produits,
AVG(prix) AS prix_moyen
FROM produits
GROUP BY gamme;
INDEX — Indexation
Un index est une structure de données qui accélère la recherche dans une table — comme l'index d'un livre. Sans index, MySQL lit chaque ligne (scan complet). Avec index : accès direct.
-- Créer un index simple
CREATE INDEX idx_email ON utilisateurs (email);
-- Index unique (comme UNIQUE + INDEX)
CREATE UNIQUE INDEX idx_email_unique ON utilisateurs (email);
-- Index composite (plusieurs colonnes)
CREATE INDEX idx_pays_age ON utilisateurs (pays, age);
-- Voir les index d'une table
SHOW INDEX FROM utilisateurs;
-- Supprimer un index
DROP INDEX idx_email ON utilisateurs;
Impact sur les performances :
N'indexez pas tout ! Chaque index ralentit les INSERT/UPDATE/DELETE (car l'index doit être mis à jour). Indexez uniquement les colonnes utilisées fréquemment dans WHERE, JOIN et ORDER BY.
EXPLAIN — Analyser une requête
EXPLAIN révèle comment MySQL exécute votre requête : quels index sont utilisés, combien de lignes sont examinées, etc.
EXPLAIN SELECT * FROM utilisateurs
WHERE email = 'aliou@gmail.com';
| Colonne EXPLAIN | Signification |
|---|---|
| type | ALL = scan complet (lent) / ref ou eq_ref = index utilisé (rapide) |
| key | Nom de l'index utilisé (NULL = aucun index) |
| rows | Estimation du nombre de lignes examinées |
| Extra | "Using index" = très rapide / "Using filesort" = tri en mémoire (à optimiser) |
Si type = ALL et rows est grand → votre requête fait un scan complet. Ajoutez un index sur la colonne dans le WHERE pour l'accélérer drastiquement.
Les Vues (VIEW)
Une vue est une table virtuelle créée à partir d'une requête. Elle ne stocke pas les données mais les recalcule à chaque appel.
-- Créer une vue
CREATE VIEW vue_clients_actifs AS
SELECT id, nom, email
FROM utilisateurs
WHERE actif = 1;
-- Utiliser la vue comme une table
SELECT * FROM vue_clients_actifs;
-- Vue complexe avec JOIN
CREATE VIEW vue_commandes_detaillees AS
SELECT
u.nom AS client,
c.montant,
c.date_commande
FROM utilisateurs u
INNER JOIN commandes c ON u.id = c.utilisateur_id;
-- Modifier une vue
CREATE OR REPLACE VIEW vue_clients_actifs AS
SELECT id, nom, email, pays
FROM utilisateurs WHERE actif = 1;
-- Supprimer une vue
DROP VIEW vue_clients_actifs;
Les vues servent à : simplifier des requêtes complexes, masquer des colonnes sensibles (mots de passe, données personnelles) pour certains utilisateurs, centraliser une logique de filtrage réutilisée partout.
Les Triggers
Un trigger s'exécute automatiquement lors d'un événement sur une table (INSERT, UPDATE, DELETE). On peut le déclencher BEFORE ou AFTER l'événement.
DELIMITER $$
CREATE TRIGGER log_inscription
AFTER INSERT ON utilisateurs
FOR EACH ROW
BEGIN
INSERT INTO journal (action, details, date_action)
VALUES ('Inscription', NEW.email, NOW());
END$$
-- Trigger BEFORE UPDATE : validation avant modification
CREATE TRIGGER verif_age
BEFORE INSERT ON utilisateurs
FOR EACH ROW
BEGIN
IF NEW.age < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Âge invalide';
END IF;
END$$
DELIMITER ;
| Événement | NEW | OLD |
|---|---|---|
| INSERT | ✅ Disponible | ❌ Non disponible |
| UPDATE | ✅ Nouvelle valeur | ✅ Ancienne valeur |
| DELETE | ❌ Non disponible | ✅ Disponible |
Procédures Stockées
Une procédure stockée est un programme SQL enregistré dans la base, réutilisable avec des paramètres entrants (IN), sortants (OUT) ou les deux (INOUT).
DELIMITER $$
CREATE PROCEDURE rechercherUtilisateur(IN recherche VARCHAR(100))
BEGIN
SELECT * FROM utilisateurs
WHERE nom LIKE CONCAT('%', recherche, '%');
END$$
-- Procédure avec paramètre OUT (retour de valeur)
CREATE PROCEDURE compterUtilisateurs(
IN p_pays VARCHAR(50),
OUT p_total INT
)
BEGIN
SELECT COUNT(*) INTO p_total
FROM utilisateurs
WHERE pays = p_pays;
END$$
DELIMITER ;
-- Appeler les procédures
CALL rechercherUtilisateur('Ali');
CALL compterUtilisateurs('Sénégal', @total);
SELECT @total;
Fonctions Personnalisées (UDF)
Une fonction personnalisée (User Defined Function) retourne une seule valeur et peut être utilisée directement dans un SELECT, WHERE ou ORDER BY.
DELIMITER $$
-- Fonction qui calcule la TVA
CREATE FUNCTION calculerTTC(prix_ht DECIMAL(10,2), taux_tva DECIMAL(5,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
RETURN prix_ht * (1 + taux_tva / 100);
END$$
DELIMITER ;
-- Utilisation dans une requête
SELECT nom, prix, calculerTTC(prix, 18) AS prix_ttc
FROM produits;
✅ Fonction (FUNCTION)
- Retourne une valeur
- Utilisable dans SELECT/WHERE
- Ne peut pas modifier des données
✅ Procédure (PROCEDURE)
- Peut retourner plusieurs résultats
- Appelée avec CALL
- Peut modifier des données
Transactions — START, COMMIT, ROLLBACK
Une transaction est un groupe d'opérations qui doivent toutes réussir ou toutes échouer. Elle garantit l'intégrité des données dans les opérations critiques (principe ACID).
START TRANSACTION;
UPDATE comptes
SET solde = solde - 5000
WHERE id = 1; -- Débit compte source
UPDATE comptes
SET solde = solde + 5000
WHERE id = 2; -- Crédit compte destination
COMMIT; -- ✅ Valider si tout réussit
-- ROLLBACK; -- ❌ Annuler tout en cas d'erreur
-- SAVEPOINT : points de restauration intermédiaires
START TRANSACTION;
INSERT INTO commandes ... ;
SAVEPOINT apres_commande;
UPDATE stocks ... ;
ROLLBACK TO apres_commande; -- Annule seulement depuis le SAVEPOINT
COMMIT;
| Commande | Effet |
|---|---|
| START TRANSACTION | Démarre une transaction |
| COMMIT | Valide — changements permanents |
| ROLLBACK | Annule tout depuis START |
| SAVEPOINT nom | Crée un point de restauration |
| ROLLBACK TO nom | Revient à ce SAVEPOINT |
Gestion des erreurs en MySQL
Dans les procédures et triggers, MySQL permet de gérer les erreurs avec des gestionnaires (HANDLER) pour éviter les arrêts brutaux.
DELIMITER $$
CREATE PROCEDURE insererUtilisateur(
IN p_nom VARCHAR(100),
IN p_email VARCHAR(150),
OUT p_succes BOOLEAN
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET p_succes = FALSE;
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO utilisateurs (nom, email) VALUES (p_nom, p_email);
SET p_succes = TRUE;
COMMIT;
END$$
DELIMITER ;
Moteurs de stockage : InnoDB vs MyISAM
MySQL supporte plusieurs moteurs de stockage. Le choix du moteur impacte les performances, la sécurité et les fonctionnalités disponibles.
| Fonctionnalité | InnoDB | MyISAM |
|---|---|---|
| Transactions (ACID) | ✅ Oui | ❌ Non |
| Clés étrangères (FK) | ✅ Oui | ❌ Non |
| Verrous de lignes | ✅ Oui | ❌ Table entière |
| Récupération crash | ✅ Automatique | ⚠️ Manuelle |
| Performances lecture | ⚡ Bonne | ⚡⚡ Très bonne |
| Full-Text Search | ✅ MySQL 5.6+ | ✅ Oui |
Utilisez toujours InnoDB (moteur par défaut depuis MySQL 5.5). Il est sûr, supporte les transactions et les clés étrangères. MyISAM ne devrait être utilisé que dans de très rares cas spécifiques de lecture intensive sans écriture concurrente.
Sécurité MySQL
La sécurité est fondamentale. Une mauvaise configuration peut exposer des données sensibles ou permettre une destruction totale de la base.
-- Créer un utilisateur dédié à l'application (ne jamais utiliser root)
CREATE USER 'app_user'@'localhost'
IDENTIFIED BY 'MotDePasse$ecur1s!';
-- Accorder uniquement les droits nécessaires (principe du moindre privilège)
GRANT SELECT, INSERT, UPDATE
ON boutique.*
TO 'app_user'@'localhost';
-- Appliquer les changements
FLUSH PRIVILEGES;
-- Voir les droits d'un utilisateur
SHOW GRANTS FOR 'app_user'@'localhost';
-- Révoquer des droits
REVOKE DELETE ON boutique.* FROM 'app_user'@'localhost';
-- Supprimer un utilisateur
DROP USER 'app_user'@'localhost';
Ne jamais :
• Utiliser root dans votre application web
• Stocker les mots de passe utilisateurs en clair (utilisez bcrypt ou argon2)
• Exposer le port 3306 directement sur Internet
• Donner des droits ALL PRIVILEGES à un utilisateur applicatif
Injection SQL — Comprendre et se protéger
L'injection SQL est l'une des attaques web les plus répandues. Elle consiste à injecter du code SQL malveillant dans les champs de formulaire pour manipuler la base.
-- Si email reçu = "admin'--" l'attaquant contourne le mot de passe
-- ❌ DANGEREUX : concaténation directe
"SELECT * FROM users WHERE email = '" + email + "'"
-- Devient : SELECT * FROM users WHERE email = 'admin'--'
-- Le -- commente le reste → accès sans mot de passe !
-- ✅ SOLUTION : Requêtes préparées (paramétrisées)
-- En PHP avec PDO :
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]); // Le paramètre est échappé automatiquement
❌ Vulnérable
- Concaténation de chaînes
- Données non validées
- Erreurs affichées aux utilisateurs
✅ Sécurisé
- Requêtes préparées (PDO, mysqli)
- Validation et sanitisation des entrées
- Gestion d'erreurs discrète
Normalisation des bases de données
La normalisation est le processus d'organisation d'une base pour réduire la redondance et améliorer l'intégrité des données. Elle suit des formes normales (NF).
| Forme Normale | Règle principale |
|---|---|
| 1NF | Valeurs atomiques (pas de listes dans une cellule), chaque colonne a un seul type |
| 2NF | Dépendance totale envers la clé primaire (pas de dépendance partielle) |
| 3NF | Pas de dépendance transitive (colonnes qui dépendent d'une autre colonne non-clé) |
| BCNF | Forme de Boyce-Codd — variante plus stricte de la 3NF |
En pratique, viser la 3NF est suffisant pour la majorité des applications. Une base bien normalisée : moins de données dupliquées, moins d'anomalies de mise à jour, structure plus cohérente.
Sauvegardes et Restauration
Une sauvegarde régulière est non-négociable en production. Une panne, une erreur humaine ou une attaque peuvent entraîner une perte totale et irréversible.
# Sauvegarder une base
mysqldump -u root -p boutique > sauvegarde_2026-06-08.sql
# Avec compression gzip
mysqldump -u root -p boutique | gzip > sauvegarde_2026-06-08.sql.gz
# Sauvegarder toutes les bases
mysqldump -u root -p --all-databases > toutes_les_bases.sql
# Sauvegarder seulement la structure (sans données)
mysqldump -u root -p --no-data boutique > structure.sql
# Restaurer une base
mysql -u root -p boutique < sauvegarde_2026-06-08.sql
# Restaurer depuis gzip
gunzip < sauvegarde_2026-06-08.sql.gz | mysql -u root -p boutique
# Automatiser avec cron (tous les jours à 2h du matin)
# 0 2 * * * mysqldump -u root -pmotdepasse boutique > /backup/$(date +%Y-%m-%d).sql
Règle 3-2-1 : 3 copies, sur 2 types de supports différents, dont 1 hors site (cloud, disque externe). Testez régulièrement vos restaurations — une sauvegarde non testée n'est pas fiable !
Réplication MySQL
La réplication copie automatiquement les données d'un serveur principal (master) vers un ou plusieurs serveurs secondaires (replicas). Elle sert à la tolérance aux pannes et à répartir la charge.
| Architecture | Description |
|---|---|
| Master → Replica | Un serveur écrit, les autres lisent uniquement |
| Master → Master | Les deux peuvent écrire (complexe, risqué) |
| Master → Relay → Replicas | Chaîne de réplication (grand nombre de replicas) |
Optimisation des Performances
Sur de grandes tables (millions de lignes), les performances deviennent critiques. Un index bien placé peut transformer une requête de 10 secondes en 10 millisecondes.
❌ Lent
- SELECT * sur grandes tables
- Pas d'index sur WHERE/JOIN
- LIKE '%mot%'
- Sous-requêtes dans des boucles
- N+1 requêtes (ORM non optimisé)
✅ Rapide
- Colonnes utiles uniquement
- INDEX sur colonnes filtrées
- LIKE 'mot%' (préfixe)
- JOIN à la place des sous-requêtes
- LIMIT sur toutes les listes
MySQL en Production — Checklist
Avant de mettre une base MySQL en production, vérifiez ces points essentiels.
| Catégorie | Action | Priorité |
|---|---|---|
| 🔒 Sécurité | Désactiver l'accès root distant | 🔴 Critique |
| 🔒 Sécurité | Pare-feu sur le port 3306 | 🔴 Critique |
| 🔒 Sécurité | Utilisateur dédié à l'app (pas root) | 🔴 Critique |
| 💾 Backup | Sauvegardes automatisées quotidiennes | 🔴 Critique |
| ⚡ Perf | Index sur toutes les FK et colonnes WHERE | 🟠 Important |
| ⚡ Perf | Activer le slow_query_log | 🟠 Important |
| 📊 Monitoring | Alertes sur CPU, mémoire, espace disque | 🟠 Important |
| 🔧 Config | Ajuster innodb_buffer_pool_size | 🟡 Recommandé |
Activez le slow query log en production : SET GLOBAL slow_query_log = 'ON'; et SET GLOBAL long_query_time = 1; — MySQL enregistrera toutes les requêtes prenant plus d'1 seconde pour que vous puissiez les optimiser.
Projet Complet — Base de Données École
Appliquons tout ce que nous avons appris en créant une base de données de gestion scolaire complète avec toutes les bonnes pratiques.
CREATE DATABASE ecole
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE ecole;
CREATE TABLE etudiants (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
prenom VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
date_naissance DATE,
cree_le TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE cours (
id INT AUTO_INCREMENT PRIMARY KEY,
titre VARCHAR(200) NOT NULL,
credits INT DEFAULT 3,
professeur VARCHAR(100),
duree_h INT
);
CREATE TABLE inscriptions (
id INT AUTO_INCREMENT PRIMARY KEY,
etudiant_id INT NOT NULL,
cours_id INT NOT NULL,
date_inscription DATETIME DEFAULT CURRENT_TIMESTAMP,
note DECIMAL(4,2) CHECK (note BETWEEN 0 AND 20),
FOREIGN KEY (etudiant_id) REFERENCES etudiants(id) ON DELETE CASCADE,
FOREIGN KEY (cours_id) REFERENCES cours(id) ON DELETE CASCADE
);
-- Étudiants avec leurs notes par cours
SELECT
CONCAT(e.prenom, ' ', e.nom) AS etudiant,
c.titre AS cours,
i.note,
CASE
WHEN i.note >= 16 THEN 'Très Bien'
WHEN i.note >= 14 THEN 'Bien'
WHEN i.note >= 10 THEN 'Passable'
ELSE 'Insuffisant'
END AS mention
FROM inscriptions i
INNER JOIN etudiants e ON i.etudiant_id = e.id
INNER JOIN cours c ON i.cours_id = c.id
ORDER BY etudiant, cours;
-- Moyenne par étudiant et classement
SELECT
CONCAT(e.prenom, ' ', e.nom) AS etudiant,
ROUND(AVG(i.note), 2) AS moyenne,
COUNT(i.cours_id) AS nb_cours
FROM etudiants e
LEFT JOIN inscriptions i ON e.id = i.etudiant_id
GROUP BY e.id
HAVING moyenne >= 10
ORDER BY moyenne DESC;
Les Bonnes Pratiques MySQL
Pour devenir un excellent développeur ou administrateur MySQL, voici les règles d'or à intégrer dans tous vos projets :
| Règle | Pourquoi c'est important | Niveau |
|---|---|---|
| ✏️ Noms explicites (snake_case) | Code lisible et maintenable | 🟢 Débutant |
| 🔑 Toujours une clé primaire | Identification unique, performance | 🟢 Débutant |
| 🌍 utf8mb4 sur toutes les bases | Support universel des caractères | 🟢 Débutant |
| 📇 Index sur FK et colonnes filtrées | Requêtes 100x plus rapides | 🟡 Intermédiaire |
| 💾 Sauvegardes automatisées | Protection contre les pertes | 🟡 Intermédiaire |
| 🔒 Principe du moindre privilège | Sécurité renforcée | 🟡 Intermédiaire |
| 🚫 Normalisation (éviter la duplication) | Cohérence et économie d'espace | 🟡 Intermédiaire |
| ⚡ Requêtes préparées (anti-injection) | Sécurité fondamentale | 🟡 Intermédiaire |
| ⏱️ Utiliser les transactions | Intégrité des opérations critiques | 🔴 Avancé |
| 🔍 EXPLAIN sur les requêtes lentes | Détecter et corriger les goulots | 🔴 Avancé |
| 📊 Surveiller le slow_query_log | Performance proactive | 🔴 Avancé |
| 🧪 Environnement dev séparé | Éviter les catastrophes prod | 🔴 Avancé |
Félicitations ! Vous avez terminé les 50 leçons MySQL — de la création d'une base aux transactions, triggers, procédures stockées et sécurité. Vous maîtrisez maintenant les fondations solides pour travailler sur des projets réels. La prochaine étape : pratiquer sur de vrais projets et explorer des sujets comme MySQL Cluster, le partitionnement ou les bases de données distribuées !