Relier nos tables dans PHPMyAdmin : organiser des données cohérentes
Dans l’article Démarrer avec PHPMyAdmin : Création de votre Première Base de Données, nous avons posé les premières briques d’un projet simple, en construisant pas à pas une table utilisateurs
. Mais une application réelle repose rarement sur une seule table. Pour suivre des commandes, associer des produits, ou croiser des informations, il nous faut une base capable de relier les données entre elles.
Dans cette suite, nous allons apprendre à créer ces liens, en comparant à chaque fois l’approche SQL et l’interface de PHPMyAdmin, avec en complément l’import de fichiers CSV lorsque c’est pertinent. Nous verrons aussi comment lire ces données liées à travers des requêtes croisées, grâce aux jointures. Notre objectif : rendre notre base à la fois logique, évolutive et solide.
Nous allons enrichir notre base de données en y ajoutant deux éléments essentiels : un catalogue de produits et un carnet de commandes. L’objectif est de relier chaque commande à un utilisateur déjà existant, et d’y associer un ou plusieurs produits. Cela nous permettra de suivre qui commande quoi, quand, et à quel prix.
Préparer notre base pour accueillir un catalogue et un carnet de commandes
Nous allons ajouter trois tables à notre base : une table produits
pour référencer les articles proposés dans notre boutique, une table commande
pour enregistrer les achats effectués par nos utilisateurs, et une table commande_details
pour lister les produits associés à chaque commande. Chaque produit aura un identifiant, un nom, une description et un prix. Chaque commande sera liée à un utilisateur, comportera une date, et pourra inclure plusieurs produits, gérés dans la table commande_details
. Cette dernière jouera le rôle de table de jonction, en assurant un lien précis entre une commande et sa composition.
Ce type d’organisation s’appuie sur les fondements du modèle relationnel, que vous pouvez retrouver dans l’article Introduction aux Bases de Données Relationnelles. Pour mieux comprendre la nature des liens que nous allons créer — un-à-plusieurs entre utilisateurs et commandes, plusieurs-à-plusieurs entre commandes et produits — vous pouvez également consulter l’article Types de Relations entre Tables dans MySQL/MariaDB.
Créer la table produits
pour accueillir notre catalogue
Commençons par structurer notre catalogue en créant une table produits
, qui contiendra un identifiant unique, un nom, un descriptif facultatif, et un prix unitaire. Si nous choisissons la voie SQL, nous pouvons nous rendre dans l’onglet SQL de PHPMyAdmin et exécuter la commande suivante :
CREATE TABLE produits (
produit_id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(100) NOT NULL,
description TEXT,
prix DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB;
L’identifiant produit_id
sera généré automatiquement, et le moteur InnoDB est requis pour que nous puissions plus tard établir des relations. Si nous préférons utiliser l’interface graphique, nous cliquons sur le nom de notre base puis sur Créer une table, que nous appelons produits
avec 4 colonnes. Nous définissons ensuite : produit_id
comme type INT
, auto-incrémenté et clé primaire ; nom
comme VARCHAR(100)
avec la case NULL décochée ; description
comme TEXT
(facultatif) ; et prix
comme DECIMAL(10,2)
en désactivant aussi NULL pour ce champ. Enfin, tout en bas du formulaire, nous veillons à ce que le moteur de stockage sélectionné soit bien InnoDB, puis nous cliquons sur Enregistrer pour finaliser la création.
Pour des explications pas à pas sur la création d’une table dans PHPMyAdmin, ainsi que sur l’import de données via un fichier CSV, vous pouvez vous référer à l’article Démarrer avec PHPMyAdmin : Création de votre Première Base de Données. Il vous sera également utile pour les prochaines étapes d’import que nous allons survoler ici.
Remplir la table produits
à l’aide d’un fichier CSV
Plutôt que de saisir nos disques un par un, nous allons préparer un fichier CSV que nous pourrons importer directement dans la table. Ce fichier contient trois colonnes : nom
, description
et prix
, séparées par des virgules, avec des guillemets autour des textes. Le champ produit_id
est volontairement absent, puisqu’il sera généré automatiquement. Voici un aperçu du format utilisé :
nom,description,prix
"Pink Floyd - Meddle","Album fondateur du virage spatial, incluant l'épique 'Echoes'.",19.99
"Can - Tago Mago","Double album hallucinatoire du krautrock expérimental allemand.",21.50
"Faust - Faust IV","Collage sonore radical et lumineux, emblématique du label Virgin naissant.",18.30
Pour remplir cette table sans saisie manuelle, nous pouvons importer un fichier CSV déjà structuré (produits_1970s.csv). Il suffit ensuite de se rendre dans l’onglet Importer de PHPMyAdmin, de choisir le fichier, de vérifier que l’encodage est en UTF-8 et que le séparateur est la virgule. Si une option permet d’utiliser la première ligne comme en-tête, pensez à l’activer.
Créer la table commande
pour relier un utilisateur à une commande
Chaque commande passée par un utilisateur sera enregistrée dans une table commande. Elle comportera un identifiant unique, un lien vers l’utilisateur (clé étrangère), et une date de création. Nous choisissons le moteur InnoDB pour pouvoir établir des relations. En SQL, voici la commande à exécuter dans l’onglet SQL de PHPMyAdmin :
CREATE TABLE commande (
commande_id INT PRIMARY KEY AUTO_INCREMENT,
utilisateur_id INT NOT NULL,
date_commande DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id)
) ENGINE=InnoDB;
L’interface graphique de PHPMyAdmin permet aussi de créer cette table manuellement. Il suffit de spécifier trois colonnes : commande_id en INT auto-incrémenté et clé primaire, utilisateur_id en INT avec la case NULL décochée, et date_commande en DATETIME avec une valeur par défaut sur CURRENT_TIMESTAMP. L’onglet Relation permettra ensuite d’ajouter la contrainte de clé étrangère sur utilisateur_id.
Enregistrer des commandes liées à nos utilisateurs
La table commande permet de suivre les achats réalisés par chaque utilisateur. Pour illustrer son fonctionnement, nous allons créer trois commandes : une pour Bob Martin, et deux pour Géraldine Simon. Chacune sera enregistrée avec une date différente pour mieux observer la chronologie des achats. L’identifiant de l’utilisateur est renseigné directement, et la date de commande peut être précisée manuellement si besoin.
-- Insertion de trois commandes dans la table 'commande'
-- Chaque ligne correspond à un achat effectué par un utilisateur
INSERT INTO commande (utilisateur_id, date_commande)
VALUES
-- Commande 1 : Bob "The Best" Martin (utilisateur_id = 2), le 20 juin 2025 à 14h30
(2, '2025-06-20 14:30:00'),
-- Commande 2 : Géraldine Simon (utilisateur_id = 7), le 21 juin 2025 à 10h15
(7, '2025-06-21 10:15:00'),
-- Commande 3 : Géraldine Simon à nouveau, le 22 juin 2025 à 9h45
(7, '2025-06-22 09:45:00');
Créer la table commandes_details
pour lister les produits de chaque commande
Une commande peut contenir plusieurs produits, et un même produit peut apparaître dans plusieurs commandes. Pour représenter cette relation de type plusieurs-à-plusieurs, nous créons une table de liaison appelée commandes_details. Chaque ligne indiquera quel produit est associé à quelle commande, et en quelle quantité. Concrètement, une commande pourra donc générer une ou plusieurs lignes dans cette table, chacune correspondant à un produit distinct figurant dans le panier. Voici la structure SQL à utiliser :
CREATE TABLE commandes_details (
id INT AUTO_INCREMENT PRIMARY KEY,
commande_id INT NOT NULL,
produit_id INT NOT NULL,
quantite INT DEFAULT 1,
FOREIGN KEY (commande_id) REFERENCES commande(commande_id),
FOREIGN KEY (produit_id) REFERENCES produits(produit_id)
) ENGINE=InnoDB;
Ajouter les clés étrangères depuis PHPMyAdmin
Une fois la table créée, il est possible de définir les contraintes de clés étrangères directement depuis PHPMyAdmin. Depuis l’onglet Structure de la table, cliquez sur Vue relationnelle en bas de page. Dans le formulaire proposé, vous pouvez spécifier pour chaque colonne : la base de données de destination, la table cible, la colonne liée, ainsi que les comportements ON DELETE et ON UPDATE (par exemple RESTRICT, CASCADE, etc.). Cette méthode permet de visualiser clairement les liens entre tables. Pour en savoir plus sur le fonctionnement de ces relations, vous pouvez consulter l’article Introduction aux Bases de Données Relationnelles.

Comprendre les options ON DELETE et ON UPDATE
Lorsqu’on crée une clé étrangère dans une table relationnelle, PHPMyAdmin propose plusieurs comportements en cas de suppression ou de modification de l’enregistrement lié.
- RESTRICT empêche toute suppression ou modification si des enregistrements liés existent.
- CASCADE supprime ou met à jour automatiquement tous les enregistrements dépendants.
- SET NULL remplace la valeur liée par
NULL
, ce qui suppose que la colonne concernée l’autorise. - Enfin, NO ACTION agit comme RESTRICT, mais laisse au moteur le soin de gérer le contrôle après exécution.
Ces choix influencent fortement la cohérence de la base : ils doivent être définis avec soin selon les usages attendus.
Remplir la table commandes_details
avec les produits commandés
Chaque ligne de la table commandes_details représente un article ajouté à une commande. Pour illustrer le fonctionnement, nous allons associer plusieurs disques à chacune de nos trois commandes : Bob Martin en commande un, et Géraldine Simon en commande plusieurs répartis sur ses deux commandes. Il suffit ici de préciser l’identifiant de la commande, celui du produit, et la quantité souhaitée.
-- Commande 1 (commande_id = 1) : Bob Martin commande 1 exemplaire du produit 3
INSERT INTO commandes_details (commande_id, produit_id, quantite)
VALUES (1, 3, 1);
-- Commande 2 (commande_id = 2) : Géraldine Simon commande 2 produits différents
INSERT INTO commandes_details (commande_id, produit_id, quantite)
VALUES
(2, 5, 1), -- 1 exemplaire du produit 5
(2, 8, 2); -- 2 exemplaires du produit 8
-- Commande 3 (commande_id = 3) : Géraldine Simon commande à nouveau, avec 3 articles
INSERT INTO commandes_details (commande_id, produit_id, quantite)
VALUES
(3, 2, 1), -- 1 exemplaire du produit 2
(3, 4, 1), -- 1 exemplaire du produit 4
(3, 7, 1); -- 1 exemplaire du produit 7
Notre base est en place, place aux requêtes relationnelles
Nous disposons désormais d’une structure complète : des utilisateurs, des commandes, un catalogue de produits, et une table de liaison pour relier tout cela. Ces quatre tables forment un ensemble relationnel cohérent, capable de modéliser des achats réels. Mais une base bien conçue n’est utile que si nous savons l’exploiter : à partir de maintenant, nous allons apprendre à formuler des requêtes adaptées, qu’il s’agisse d’enregistrer une commande ou de retrouver l’historique d’un utilisateur.
Cela passera par des lectures croisées, des jointures entre tables, des conditions d’insertion, et parfois des adaptations en fonction du langage utilisé. Nous allons explorer pas à pas ces opérations, à commencer par une requête simple : retrouver les produits associés à une commande.
Avant de passer aux requêtes
Bien que notre base puisse désormais être utilisée depuis n’importe quelle interface (application web, script, formulaire…), nous allons, pour l’instant, nous concentrer uniquement sur les requêtes SQL. Cela nous permettra de comprendre la logique des relations sans dépendre d’un langage tiers ou d’un outil de développement. Nous pourrons ainsi observer précisément comment les tables interagissent, comment les données circulent entre elles, et comment les résultats sont structurés. Les interfaces graphiques comme PHPMyAdmin ou les bibliothèques de requêtage en PHP pourront venir ensuite, mais la maîtrise directe du SQL reste la base indispensable.
Lire les produits associés à une commande
Pour connaître le détail d’une commande, nous devons consulter la table commandes_details
qui relie les produits à la commande concernée. Chaque ligne y indique un produit commandé, sa quantité, et une référence vers la commande. Mais pour obtenir des informations utiles comme le nom du produit ou son prix, nous devons croiser cette table avec celle des produits
. C’est ici qu’intervient la jointure (JOIN) : elle permet de combiner les informations des deux tables à partir de la clé produit_id
.
Voici un exemple de requête SQL pour afficher les produits d’une commande donnée (par exemple, la commande 2) :
SELECT
p.nom AS nom_produit,
p.prix,
cd.quantite,
(p.prix * cd.quantite) AS total_ligne
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
WHERE cd.commande_id = 2;
Cette requête affiche, pour la commande 2, le nom de chaque produit commandé, son prix unitaire, la quantité achetée, et le total correspondant.
Nous pourrons enrichir cette lecture ensuite en y ajoutant la date de commande, le nom de l’utilisateur, ou d’autres éléments issus des tables liées.

Afficher le détail complet d’une commande
Nous pouvons maintenant compléter cette requête pour afficher non seulement les produits d’une commande, mais aussi des informations générales sur la commande elle-même et sur l’utilisateur concerné. Pour cela, il suffit d’ajouter deux nouvelles jointures : l’une vers la table commande
, l’autre vers la table utilisateurs
.
SELECT
u.nom AS nom_utilisateur,
c.date_commande,
p.nom AS nom_produit,
p.prix,
cd.quantite,
(p.prix * cd.quantite) AS total_ligne
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
JOIN commande c ON cd.commande_id = c.commande_id
JOIN utilisateurs u ON c.utilisateur_id = u.id
WHERE cd.commande_id = 2;
Cette requête croise quatre tables pour fournir un récapitulatif clair : on y retrouve le nom de l’acheteur, la date de la commande, les articles achetés, leurs prix, les quantités commandées, et le total pour chaque ligne. Il devient ainsi possible d’obtenir des extraits très détaillés sans répéter l’information, tout en s’appuyant sur la structure relationnelle que nous avons construite.

Il est parfois plus pratique de filtrer directement sur l’identifiant de l’utilisateur (utilisateur_id
) plutôt que sur son nom. Cela évite les problèmes liés à des homonymes ou à des variations d’orthographe. En modifiant simplement la clause WHERE
, nous pouvons obtenir exactement le même résultat, mais de manière plus sûre :
SELECT
u.nom AS nom_utilisateur,
c.commande_id,
c.date_commande,
p.nom AS nom_produit,
p.prix,
cd.quantite,
(p.prix * cd.quantite) AS total_ligne
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
JOIN commande c ON cd.commande_id = c.commande_id
JOIN utilisateurs u ON c.utilisateur_id = u.id
WHERE c.utilisateur_id = 7;
Cette requête liste donc tout l’historique des achats de Géraldine Simon, sans ambiguïté. Le champ commande_id
, affiché dans le tableau, permet de distinguer visuellement les différentes commandes dans le résultat.

Lire une commande dans son contexte : ligne, total partiel, total global
Il est souvent utile de ne pas se limiter au simple détail des lignes commandées. Pour mieux comprendre le poids d’un article dans une commande, ou d’une commande dans l’ensemble des achats d’un utilisateur, nous pouvons enrichir notre requête avec plusieurs niveaux de totalisation. Chaque ligne du tableau affichera ainsi :
- le montant de la ligne (quantité × prix),
- le total de la commande à laquelle cette ligne appartient,
- et le total global des commandes de l’utilisateur.
Décortiquons le principe des sous-requêtes
Une sous-requête est tout simplement une requête SQL imbriquée à l’intérieur d’une autre. Elle se comporte comme une petite table générée à la volée : MySQL l’exécute d’abord, puis injecte son résultat dans la requête principale. Pour bien saisir le mécanisme, partons d’un besoin simple : calculer le total d’une commande sans perdre le détail de ses lignes.
Nous sélectionnons d’abord une ligne de commande, son prix et la quantité, c’est que nous avions extrait lorsque nous avions requis les produits associés à une commande :
SELECT
cd.commande_id,
p.nom,
cd.quantite,
p.prix,
(p.prix * cd.quantite) AS total_ligne
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
WHERE cd.commande_id = 2;
Le résultat nous donne chaque article et le montant correspondant, mais pas le total global de la commande.

Pour l’afficher dans la même sortie, nous ajoutons une sous-requête dans la clause SELECT
; elle s’exécute pour chaque ligne et cumule les montants de tous les articles partageant le même commande_id
:
SELECT
cd.commande_id,
p.nom,
cd.quantite,
p.prix,
(p.prix * cd.quantite) AS total_ligne,
(
SELECT SUM(p2.prix * cd2.quantite)
FROM commandes_details cd2
JOIN produits p2 ON cd2.produit_id = p2.produit_id
WHERE cd2.commande_id = cd.commande_id
) AS total_commande
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
WHERE cd.commande_id = 2;

Chaque ligne dévoile désormais le total de la ligne et le total de la commande ; la sous-requête joue le rôle d’un calcul à la volée, sans perturber l’affichage détaillé.
Il nous reste à incorporer le total cumulé de toutes les commandes de l’utilisateur. Nous imbriquons une seconde sous-requête, cette fois-ci en remontant la relation jusqu’à la table commande
, puis jusqu’à utilisateurs
. Le mécanisme est identique : on calcule, on injecte, on affiche.
SELECT
u.nom AS nom_utilisateur,
c.commande_id,
c.date_commande,
p.nom AS nom_produit,
p.prix,
cd.quantite,
(p.prix * cd.quantite) AS total_ligne,
(
SELECT SUM(p2.prix * cd2.quantite)
FROM commandes_details cd2
JOIN produits p2 ON cd2.produit_id = p2.produit_id
WHERE cd2.commande_id = c.commande_id
) AS total_commande,
(
SELECT SUM(p3.prix * cd3.quantite)
FROM commandes_details cd3
JOIN produits p3 ON cd3.produit_id = p3.produit_id
JOIN commande c3 ON cd3.commande_id = c3.commande_id
WHERE c3.utilisateur_id = u.id
) AS total_global_utilisateur
FROM commandes_details cd
JOIN produits p ON cd.produit_id = p.produit_id
JOIN commande c ON cd.commande_id = c.commande_id
JOIN utilisateurs u ON c.utilisateur_id = u.id
WHERE c.utilisateur_id = 7;

Nous obtenons alors, pour chaque article : le total ligne, le total de la commande, et le total de l’utilisateur. Les sous-requêtes vivent le temps de l’instruction, s’exécutent isolément, puis disparaissent, laissant un tableau final complet et parfaitement lisible.
Ajouter une nouvelle commande
Imaginons que Géraldine Simon passe une nouvelle commande ce matin du 21 juin. Elle achète trois albums issus de notre catalogue :
- Phaedra de Tangerine Dream (produit_id = 2)
- In the Court of the Crimson King de King Crimson (produit_id = 5)
- Flying Teapot de Gong (produit_id = 9)
Elle en commande respectivement 1, 2 et 1 exemplaire.
Commençons par insérer la commande dans la table commande
. Géraldine ayant pour identifiant utilisateur_id = 7
, la requête est :
INSERT INTO commande (utilisateur_id, date_commande)
VALUES (7, '2025-06-21 11:30:00');
Dès que cette requête est exécutée, MySQL crée une nouvelle ligne dans commande
et génère automatiquement un nouvel identifiant (commande_id
). Pour le récupérer immédiatement après dans le même script ou environnement SQL, on peut utiliser :
SET @id_commande := LAST_INSERT_ID();
Cela nous permet de mémoriser l’identifiant généré dans une variable temporaire @id_commande
utilisable pour les lignes suivantes.
Ajouter les articles commandés dans la table commandes_details
Nous pouvons maintenant insérer les trois lignes correspondant aux produits achetés par Géraldine, en utilisant la variable stockée :
INSERT INTO commandes_details (commande_id, produit_id, quantite)
VALUES
(@id_commande, 2, 1), -- Phaedra, 1 exemplaire
(@id_commande, 5, 2), -- In the Court of the Crimson King, 2 exemplaires
(@id_commande, 9, 1); -- Flying Teapot, 1 exemplaire
Chaque ligne référence la commande que nous venons de créer (@id_commande
) et rattache un produit précis avec la quantité souhaitée. Ces instructions peuvent être exécutées en bloc dans l’interface SQL de PHPMyAdmin ou dans un script de traitement automatisé.
Attention : maintenir la variable dans la même session
Si vous utilisez PHPMyAdmin pour exécuter ces requêtes, veillez à les lancer dans la même session (idéalement dans un seul onglet SQL, ou en les regroupant dans un même bloc). Sinon, la variable @id_commande
risque d’être perdue entre deux exécutions : PHPMyAdmin ne conserve pas les variables temporaires d’une requête à l’autre.
Ainsi, pour que tout fonctionne correctement, vous pouvez regrouper l’ensemble des instructions dans un même bloc SQL :
INSERT INTO commande (utilisateur_id, date_commande)
VALUES (7, '2025-06-21 11:30:00');
SET @id_commande := LAST_INSERT_ID();
INSERT INTO commandes_details (commande_id, produit_id, quantite)
VALUES
(@id_commande, 2, 1),
(@id_commande, 5, 2),
(@id_commande, 9, 1);
Cela garantit que la variable est bien disponible au moment de l’insertion des produits.
Vérifier l’insertion dans la commande de Géraldine
Une fois l’ajout effectué, nous pouvons relancer la requête vue plus haut dans Lire une commande dans son contexte : ligne, total partiel, total global pour observer la mise à jour des résultats. Géraldine ayant déjà deux commandes dans notre exemple, cette nouvelle commande apparaîtra automatiquement dans le tableau, avec ses articles, ses montants par ligne, son total de commande, et son total cumulé :

Nous retrouvons ainsi la commande tout juste insérée, intégrée dans l’historique de Géraldine avec ses totaux mis à jour automatiquement.
Et maintenant ?
Nous disposons désormais d’un véritable mini-système de gestion de commandes, structuré, peuplé, et interrogeable. Les tables sont liées, les données cohérentes, et les requêtes permettent d’extraire aussi bien les détails que les totaux. Il ne reste plus qu’à imaginer comment ces manipulations pourraient s’intégrer dans une interface web, un tableau de bord, ou un outil de suivi plus complet. À ce stade, tout peut commencer.