Une base de données relationnelle organise les données en tables reliées entre elles par des clés, et on les interroge avec SQL. C'est le modèle dominant depuis 50 ans pour les applications qui ont besoin de cohérence, d'intégrité et de requêtes structurées. Trois moteurs couvrent l'essentiel des besoins : SQLite (base embarquée dans un fichier), PostgreSQL (moteur objet-relationnel extensible) et MySQL (serveur SQL très répandu sur le web). Ce guide pose les bases du modèle relationnel, puis compare concrètement ces trois moteurs pour vous aider à choisir.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre le modèle relationnel : tables, colonnes, clés primaires et étrangères
- Écrire des requêtes SQL de base : créer une table, insérer, interroger, joindre
- Connaître les garanties ACID et ce qu'elles signifient en pratique
- Différencier SQLite, PostgreSQL et MySQL sur le typage, les transactions, l'administration et les cas d'usage
- Choisir le bon moteur selon votre contexte (script local, application web, données complexes)
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Dès qu'une application a besoin de stocker des données structurées, comptes utilisateurs, commandes, inventaire, logs, configuration, la question du moteur de base de données se pose. Vous rencontrerez ce choix dans des situations très concrètes :
- Vous écrivez un script d'inventaire ou un outil CLI et vous voulez stocker les résultats localement
- Vous déployez une application web qui gère des utilisateurs, des produits et des commandes
- Vous montez un service métier avec des données complexes, des contraintes d'intégrité forte et des besoins d'extensibilité
- Vous administrez un serveur et vous devez choisir entre SQLite, PostgreSQL et MySQL pour un nouveau projet
Qu'est-ce qu'une base relationnelle ?
Section intitulée « Qu'est-ce qu'une base relationnelle ? »Une base de données relationnelle stocke les informations dans des tables. Chaque table représente un type d'objet (un utilisateur, un produit, une commande). Chaque ligne est un enregistrement, chaque colonne est un attribut.
Ce modèle a été formalisé en 1970 par Edgar F. Codd (chercheur chez IBM) dans son article fondateur sur le modèle relationnel. Le langage SQL (Structured Query Language) est né dans la foulée pour interroger ces tables. Cinquante ans plus tard, SQL reste le standard universel.
Les briques essentielles
Section intitulée « Les briques essentielles »Ces six notions suffisent à lire n'importe quel schéma de base relationnelle,
et les deux dernières sont celles qui font la différence avec un simple fichier
CSV. La clé primaire garantit qu'aucune ligne ne peut être confondue avec
une autre, la clé étrangère et les contraintes délèguent au moteur des
règles que sinon chaque application devrait réimplémenter. Le point à retenir
en lisant le tableau : ces règles sont vérifiées à l'écriture, donc une
donnée invalide est rejetée au moment de l'INSERT, pas découverte six mois
plus tard dans un rapport.
| Concept | Rôle | Exemple |
|---|---|---|
| Table | Structure qui contient les données | clients, commandes |
| Colonne | Attribut d'une table (nom + type) | nom VARCHAR(100), prix DECIMAL(10,2) |
| Ligne | Un enregistrement | Le client "Alice", la commande n° 42 |
| Clé primaire | Identifiant unique d'une ligne | id INT PRIMARY KEY |
| Clé étrangère | Lien vers la clé primaire d'une autre table | client_id REFERENCES clients(id) |
| Contrainte | Règle d'intégrité (unicité, non-null, référence) | UNIQUE, NOT NULL, FOREIGN KEY |
Relations entre tables
Section intitulée « Relations entre tables »Les tables ne vivent pas isolées. Une clé étrangère relie une table à une autre :
Dans cet exemple, chaque commande pointe vers un client via client_id. C'est
l'intégrité référentielle : impossible de créer une commande pour un client
qui n'existe pas.
SQL en pratique : un exemple de bout en bout
Section intitulée « SQL en pratique : un exemple de bout en bout »Plutôt que des extraits isolés, voici un mini-domaine complet, clients, produits, commandes, pour voir les opérations fondamentales en action.
Créer les tables
Section intitulée « Créer les tables »L'ordre de création compte : commandes référence clients et produits, ces
deux tables doivent donc exister avant. Regardez surtout les mots-clés placés
après le type de chaque colonne, ce sont eux qui portent les garanties.
NOT NULL interdit une ligne incomplète, UNIQUE empêche deux comptes avec le
même e-mail, et FOREIGN KEY bloque une commande rattachée à un client
inexistant. Le type DECIMAL(10, 2) est délibéré pour un prix : contrairement
à FLOAT, il stocke une valeur exacte et évite les erreurs d'arrondi sur les
montants.
CREATE TABLE clients ( id INT PRIMARY KEY, nom VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE);
CREATE TABLE produits ( id INT PRIMARY KEY, nom VARCHAR(100) NOT NULL, prix DECIMAL(10, 2) NOT NULL);
CREATE TABLE commandes ( id INT PRIMARY KEY, client_id INT NOT NULL, produit_id INT NOT NULL, quantite INT DEFAULT 1, date_commande DATE NOT NULL, FOREIGN KEY (client_id) REFERENCES clients(id), FOREIGN KEY (produit_id) REFERENCES produits(id));Un point à connaître avant de rejouer cet exemple : SQLite n'applique pas
les clés étrangères par défaut. Il faut exécuter PRAGMA foreign_keys = ON;
sur chaque connexion, sinon l'insertion d'une commande pointant vers un client
inexistant passe sans erreur. PostgreSQL et MySQL/InnoDB les vérifient
d'office.
Insérer des données
Section intitulée « Insérer des données »Les lignes sont insérées dans le même ordre que les tables ont été créées, et
pour la même raison : la contrainte FOREIGN KEY refuserait une commande dont
le client n'existe pas encore. Notez la syntaxe multi-valeurs, une seule
instruction INSERT pour plusieurs lignes. Elle n'est pas qu'un raccourci
d'écriture : les lignes sont insérées dans la même transaction, ce qui
évite à la base d'écrire son journal une fois par ligne. Sur un chargement de
plusieurs milliers d'enregistrements, la différence se compte en minutes.
INSERT INTO clients (id, nom, email) VALUES (1, 'Alice Martin', 'alice@example.com'), (2, 'Bob Dupont', 'bob@example.com');
INSERT INTO produits (id, nom, prix) VALUES (1, 'Ordinateur portable', 999.99), (2, 'Clavier mécanique', 89.50);
INSERT INTO commandes (id, client_id, produit_id, quantite, date_commande) VALUES (1, 1, 1, 1, '2026-04-10'), (2, 1, 2, 2, '2026-04-11'), (3, 2, 2, 1, '2026-04-12');Interroger avec une jointure
Section intitulée « Interroger avec une jointure »La jointure (JOIN) est la requête qui tire parti des relations entre
tables. Elle permet de combiner les données de plusieurs tables en une seule
réponse :
SELECT c.nom AS client, p.nom AS produit, cmd.quantite, p.prix * cmd.quantite AS totalFROM commandes cmdJOIN clients c ON cmd.client_id = c.idJOIN produits p ON cmd.produit_id = p.idORDER BY cmd.date_commande;Résultat :
client | produit | quantite | total-----------------+---------------------+----------+-------- Alice Martin | Ordinateur portable | 1 | 999.99 Alice Martin | Clavier mécanique | 2 | 179.00 Bob Dupont | Clavier mécanique | 1 | 89.50Modifier et supprimer
Section intitulée « Modifier et supprimer »La clause WHERE est ce qu'il faut regarder en premier sur ces deux
instructions. Sans elle, UPDATE produits SET prix = 849.99 réécrit toutes
les lignes de la table et DELETE FROM commandes la vide intégralement, sans
demande de confirmation. Le réflexe qui évite l'accident consiste à écrire
d'abord la requête en SELECT avec le même WHERE, à vérifier le nombre de
lignes renvoyées, puis à remplacer SELECT par UPDATE ou DELETE. Un
DELETE sur clients échouerait ici tant que des commandes y font référence,
c'est précisément le rôle de la clé étrangère.
-- Mettre à jour un prixUPDATE produits SET prix = 849.99 WHERE id = 1;
-- Supprimer une commandeDELETE FROM commandes WHERE id = 3;Ce que garantit un SGBDR
Section intitulée « Ce que garantit un SGBDR »Un système de gestion de bases de données relationnelles (SGBDR) ne se contente pas de stocker des lignes. Il offre des garanties qui le distinguent d'un simple fichier CSV ou JSON.
Transactions et propriétés ACID
Section intitulée « Transactions et propriétés ACID »Une transaction regroupe plusieurs opérations en un bloc atomique :
BEGIN;UPDATE comptes SET solde = solde - 100 WHERE id = 1;UPDATE comptes SET solde = solde + 100 WHERE id = 2;COMMIT;Si l'une des opérations échoue, aucune n'est appliquée (ROLLBACK). C'est
le principe fondamental des propriétés ACID :
| Propriété | Signification | En pratique |
|---|---|---|
| Atomicité | Tout ou rien | Un virement ne peut pas débiter sans créditer |
| Cohérence | La base passe d'un état valide à un autre | Les contraintes sont toujours respectées |
| Isolation | Les transactions concurrentes ne se perturbent pas | Deux virements simultanés donnent le bon résultat |
| Durabilité | Un COMMIT est permanent | Même après un crash, les données sont là |
Un index est une structure de données maintenue à part de la table, qui conserve les valeurs d'une ou plusieurs colonnes triées, avec un pointeur vers la ligne correspondante. Sans index, le moteur lit la table entière pour trouver une valeur, c'est le scan séquentiel : son coût croît proportionnellement au nombre de lignes. Avec un index, le moteur descend dans l'arbre trié en quelques lectures seulement, quelle que soit la taille de la table.
CREATE INDEX idx_clients_email ON clients(email);Cette accélération se paie à l'écriture : chaque INSERT, UPDATE ou DELETE
doit aussi mettre l'index à jour, et l'index occupe de l'espace disque. Un
index posé sur une colonne jamais filtrée coûte donc sans rien rapporter.
Le type d'index le plus courant est le B-Tree, efficace pour les recherches
d'égalité (WHERE email = '...') et par plage (WHERE prix > 50).
PostgreSQL propose aussi Hash, GiST, SP-GiST, GIN, BRIN et des index sur
expressions. Sur MySQL, le moteur InnoDB n'accepte que des index B-Tree :
un USING HASH déclaré y est silencieusement converti en B-Tree, les vrais
index hash n'existent que sur les moteurs MEMORY et NDB. SQLite ne
propose que le B-Tree pour les index classiques.
Contrôle d'accès
Section intitulée « Contrôle d'accès »La gestion des utilisateurs et des permissions diffère fondamentalement d'un moteur à l'autre. Cette différence est celle qui surprend le plus lors d'une migration, parce qu'elle ne se voit pas dans le schéma : les tables se transfèrent, les droits non. Les trois onglets ci-dessous montrent le même besoin, donner un accès en lecture seule à une utilisatrice nommée Alice, et trois façons incompatibles d'y répondre.
PostgreSQL raisonne en rôles. Un rôle peut être un utilisateur (avec
LOGIN) ou un groupe (sans LOGIN). Les permissions se gèrent avec GRANT et
REVOKE :
CREATE ROLE lecteur;GRANT SELECT ON ALL TABLES IN SCHEMA public TO lecteur;
CREATE ROLE alice LOGIN PASSWORD 'motdepasse';GRANT lecteur TO alice;MySQL associe un utilisateur à un hôte ('user'@'host'). Les
permissions se gèrent aussi avec GRANT et REVOKE, mais la syntaxe diffère :
CREATE USER 'alice'@'%' IDENTIFIED BY 'motdepasse';GRANT SELECT ON labdb.* TO 'alice'@'%';SQLite n'a pas de gestion d'utilisateurs. C'est une bibliothèque embarquée : le contrôle d'accès repose sur les permissions du fichier au niveau du système d'exploitation :
# Seul l'utilisateur app peut lire/écrire la basechmod 600 /var/lib/monapp/data.dbchown app:app /var/lib/monapp/data.dbSQLite, PostgreSQL ou MySQL ?
Section intitulée « SQLite, PostgreSQL ou MySQL ? »C'est la question centrale. Ces trois moteurs sont tous relationnels et parlent SQL, mais ils n'ont pas la même nature ni les mêmes cas d'usage.
Vue d'ensemble
Section intitulée « Vue d'ensemble »La première ligne du tableau explique presque toutes les autres. SQLite n'est pas un serveur mais une bibliothèque liée à votre programme : il n'y a aucun processus à démarrer, donc aucun compte à créer, aucun port à ouvrir et aucune réplication à configurer. PostgreSQL et MySQL sont des services réseau, ce qui leur ouvre le multi-utilisateurs et la haute disponibilité au prix d'une installation et d'une administration. La ligne Licence mérite aussi un arrêt si le projet est commercial : MySQL est distribué sous double licence, GPL v2 pour l'édition Community et licence commerciale pour l'Enterprise, alors que SQLite et PostgreSQL n'imposent aucune contrainte de ce type.
| Critère | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| Nature | Bibliothèque embarquée (fichier local) | Serveur objet-relationnel extensible | Serveur SQL multi-utilisateur |
| Déploiement | Aucun, un fichier suffit | Service à installer et configurer | Service à installer et configurer |
| Multi-utilisateurs | Un seul écrivain à la fois | Oui, conçu pour | Oui, conçu pour |
| Typage | Dynamique (par valeur), mode STRICT optionnel | Statique, strict, types riches (JSON, array, range, hstore) | Statique, strict, types classiques |
| Extensibilité | Limitée | Très riche (extensions, types custom, PL/pgSQL, PL/Python) | Plugins, UDF |
| Réplication | Non intégrée | Streaming, logique, synchrone/asynchrone | Source/replica, Group Replication |
| Licence | Domaine public | PostgreSQL License (libre) | GPL v2 (Community) / Commercial (Enterprise) |
| Taille typique | Ko → quelques Go | Go → To | Go → To |
Quand choisir SQLite
Section intitulée « Quand choisir SQLite »SQLite est une bibliothèque C qui lit et écrit directement dans un fichier. Il n'y a pas de serveur, pas de configuration réseau, pas de gestion d'utilisateurs.
Cas d'usage typiques :
- Scripts et outils CLI qui stockent des résultats ou de la configuration
- Applications mobiles et embarquées
- Prototypage rapide et tests
- Fichiers de données locales (inventaire, cache, historique)
Limites :
- Un seul processus peut écrire à la fois (sérialisé, même en mode WAL)
- Pas de gestion d'accès réseau ni d'authentification
- Le typage est dynamique par défaut, une colonne
INTpeut contenir du texte siSTRICTn'est pas activé
Quand choisir PostgreSQL
Section intitulée « Quand choisir PostgreSQL »PostgreSQL est un moteur objet-relationnel avec une philosophie d'extensibilité et de conformité aux standards SQL. C'est le choix le plus riche fonctionnellement.
Cas d'usage typiques :
- Applications métier avec des données complexes (géospatiales, JSON, arrays)
- Systèmes nécessitant une intégrité forte et des contraintes avancées
- Projets avec des besoins d'extensibilité (types custom, extensions comme PostGIS, pgvector)
- Environnements DevOps / infrastructure (Terraform state, Vault, AWX, Gitea)
Points forts différenciants :
- Types avancés natifs :
JSONB,ARRAY,RANGE,UUID,INET - Index spécialisés : GiST, GIN, BRIN
- Réplication streaming et logique intégrée
EXPLAIN ANALYZEtrès détaillé pour l'optimisation
Quand choisir MySQL
Section intitulée « Quand choisir MySQL »MySQL est un serveur SQL très répandu, historiquement dominant dans l'écosystème web (LAMP, WordPress, Drupal, Magento). Il est simple à démarrer et dispose d'un large écosystème d'hébergement.
Cas d'usage typiques :
- Applications web classiques (CMS, e-commerce, blogs)
- Projets nécessitant un hébergement mutualisé (MySQL est quasi universel chez les hébergeurs)
- Stacks applicatives existantes qui s'appuient sur MySQL/MariaDB
- Réplication source/replica pour la lecture distribuée
Points à connaître :
- La terminologie a évolué : on parle désormais de source et replica (et non plus maître/esclave)
- La commande
CHANGE REPLICATION SOURCE TOremplaceCHANGE MASTER TO(déprécié depuis MySQL 8.0.23) - MariaDB est un fork de MySQL avec des divergences croissantes
Tableau de décision rapide
Section intitulée « Tableau de décision rapide »Ce tableau se lit en partant de votre contrainte la plus rigide, pas de vos préférences. Si l'application doit tenir dans un hébergement mutualisé, le choix est fait par l'hébergeur. Si elle est embarquée ou distribuée sous forme d'un binaire, elle ne peut pas exiger un service tiers. Les lignes qui laissent une vraie latitude sont celles où plusieurs moteurs conviennent, et le critère qui tranche alors est le plus souvent la compétence de l'équipe plutôt qu'une caractéristique technique : un PostgreSQL mal exploité rend un moins bon service qu'un MySQL bien maîtrisé.
| Votre contexte | Moteur recommandé |
|---|---|
| Script local, outil CLI, prototypage | SQLite |
| Application web simple, hébergement mutualisé | MySQL |
| Application métier, données complexes, DevOps | PostgreSQL |
| Mobile, embarqué, IoT | SQLite |
| Intégrité forte, extensions, standards SQL | PostgreSQL |
| Stack existante MySQL/MariaDB | MySQL |
| Besoin de réplication avancée | PostgreSQL ou MySQL |
À retenir
Section intitulée « À retenir »- Une base relationnelle organise les données en tables reliées par des clés, interrogées en SQL
- Les opérations SQL de base,
CREATE TABLE,INSERT,SELECT JOIN,UPDATE,DELETE, sont portables entre moteurs - Les propriétés ACID garantissent la fiabilité des transactions, mais le comportement exact de l'isolation varie selon le moteur
- SQLite est une bibliothèque embarquée (fichier local, zéro configuration, un seul écrivain), idéale pour scripts et outils
- PostgreSQL est un moteur objet-relationnel extensible (types riches, index spécialisés, réplication intégrée), le plus complet fonctionnellement
- MySQL est un serveur SQL très répandu (écosystème web, hébergement facile, réplication source/replica), le plus courant en hébergement
- Le typage diffère : dynamique par défaut sur SQLite, strict sur PostgreSQL et MySQL
- Le contrôle d'accès diffère radicalement : rôles (PostgreSQL), user@host (MySQL), permissions fichier (SQLite)