🧚 Écoute ! Beekeeper Studio est une interface de base de données rapide, moderne et open source Télécharger
7 janvier 2026 Par Matthew Rathbone

Une relation un-à-plusieurs relie une ligne d’une table parente à un nombre quelconque de lignes d’une table enfant, comme un client et ses commandes. On la construit avec une colonne de clé étrangère dans la table enfant qui pointe vers le parent. Les exemples fonctionnent sous PostgreSQL, MySQL, SQLite et SQL Server.

Comment modéliser une relation un-à-plusieurs

Ajoute à la table enfant, le côté « plusieurs », une colonne qui contient la clé primaire de son parent, et déclare-la comme clé étrangère. Chaque commande stocke l’id du client qui l’a passée, et la table des clients ne change pas du tout.

Diagramme d’une relation un-à-plusieurs en base de données

CREATE TABLE customers (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

-- Each order stores the id of the customer who placed it
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    order_date DATE NOT NULL,
    total DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

Joins les deux tables sur la clé étrangère pour voir chaque client avec ses commandes :

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT c.name, o.order_date, o.total
FROM customers c
JOIN orders o ON c.id = o.customer_id
ORDER BY c.name, o.order_date;

-- Output:
-- name  | order_date | total
-- ------+------------+-------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-03-02 | 42.50
-- Bob   | 2026-04-11 | 120.00
-- Bob   | 2026-05-20 | 64.99

Bob a trois commandes et chaque commande a un seul client.

Règles à connaître

  • La clé étrangère va dans la table enfant. Chaque commande enregistre son client. Ne donne jamais à customers une colonne qui liste des ID de commandes.
  • Un simple JOIN écarte les parents sans enfants. Carol n’apparaît pas dans le résultat ci-dessus. Utilise un LEFT JOIN quand chaque parent doit apparaître, comme dans les exemples plus bas.
  • Indexe la clé étrangère. Les jointures et les recherches par client ont besoin d’un index sur orders.customer_id (MySQL en crée un pour toi avec la clé étrangère) :
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
  • La clé étrangère protège les deux tables. Elle rejette une commande pour un client qui n’existe pas et, par défaut, refuse de supprimer un client qui a encore des commandes. Ajoute ON DELETE CASCADE pour supprimer ses commandes avec lui, ou ON DELETE SET NULL pour garder les commandes sans client (la colonne doit alors accepter NULL) :
-- Input: the customers and orders tables above

INSERT INTO orders VALUES (5, 99, '2026-06-01', 10.00);
-- ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"

DELETE FROM customers WHERE id = 1;
-- ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"

-- With FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE
DELETE FROM customers WHERE id = 1;

-- Output (orders after the DELETE):
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99
  • SQLite ignore les clés étrangères tant que tu ne les actives pas. Exécute PRAGMA foreign_keys = ON; sur chaque connexion, sinon SQLite accepte des commandes pour des clients qui n’existent pas.

Syntaxe alternative

Il existe trois autres façons de déclarer la même clé :

-- Inline on the column (MySQL 8.0 accepts this but silently ignores it)
customer_id INTEGER NOT NULL REFERENCES customers(id)

-- Named, so errors and later changes can refer to it
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers(id)

-- Added to an existing table (SQLite can't add a foreign key this way)
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer
    FOREIGN KEY (customer_id) REFERENCES customers(id);

Pour des id remplis automatiquement par la base de données, déclare la clé du parent en SERIAL PRIMARY KEY sous PostgreSQL, INT AUTO_INCREMENT PRIMARY KEY sous MySQL, INTEGER PRIMARY KEY sous SQLite ou INT IDENTITY PRIMARY KEY sous SQL Server. La colonne de clé étrangère reste un simple entier.

Quand utiliser une relation un-à-plusieurs ?

Utilise-la dès que chaque enfant appartient à exactement un parent : clients et commandes, articles et commentaires, services et employés, catégories et produits. Si un enfant peut avoir plusieurs parents, comme un étudiant qui suit plusieurs cours, il te faut plutôt une relation plusieurs-à-plusieurs. Voici les problèmes qu’une relation un-à-plusieurs permet de résoudre.

Afficher chaque parent avec ses enfants

Une facture avec ses lignes, un article avec ses commentaires : joins la table enfant au parent sur la clé étrangère. Les colonnes du parent se répètent sur chaque ligne enfant.

Compter les enfants de chaque parent

Combien de commandes chaque client a passées, combien d’articles chaque auteur a écrits : LEFT JOIN, regroupe par parent et compte une colonne enfant. Compter une colonne enfant plutôt que * fait apparaître les parents sans enfants avec 0.

Trouver les parents sans enfants

Les clients qui n’ont jamais commandé, les services sans projets, les produits que personne n’a évalués. Un LEFT JOIN remplit les colonnes enfant avec NULL quand il n’y a pas de correspondance : garde donc les lignes où l’id de l’enfant est NULL.

Totaliser les valeurs des enfants

Chiffre d’affaires par client, valeur du stock par catégorie, heures saisies par projet : SUM, AVG, MIN ou MAX sur les lignes enfant, regroupées par parent. Un parent sans enfants obtient NULL, que COALESCE transforme en 0.

Stocker une hiérarchie dans une seule table

Les employés et leurs managers, les dossiers et sous-dossiers, les catégories et sous-catégories : la clé étrangère pointe vers la même table, et un NULL marque le sommet de l’arbre. Joins la table à elle-même pour placer chaque ligne à côté de son parent.

Exemples concrets de relations un-à-plusieurs

Afficher la dernière commande de chaque client

ROW_NUMBER() numérote les commandes de chaque client de la plus récente à la plus ancienne. Garde le numéro 1 :

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT name, order_date, total
FROM (
    SELECT c.name, o.order_date, o.total,
           ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.order_date DESC) AS rn
    FROM customers c
    JOIN orders o ON c.id = o.customer_id
) latest
WHERE rn = 1
ORDER BY name;

-- Output:
-- name  | order_date | total
-- ------+------------+------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-05-20 | 64.99

Classer les clients par nombre de commandes

-- Input: the customers and orders tables above

SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY order_count DESC;

-- Output:
-- name  | order_count
-- ------+------------
-- Bob   | 3
-- Alice | 1
-- Carol | 0

Trouver les clients qui n’ont jamais commandé

-- Input: the customers and orders tables above

SELECT c.name
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL;

-- Output:
-- name
-- -----
-- Carol

NOT EXISTS fait le même travail et colle davantage à la question :

-- Input: the customers and orders tables above

SELECT c.name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.id
);

-- Output:
-- name
-- -----
-- Carol

Trouver tes plus gros clients

SUM sur aucune ligne donne NULL, donc COALESCE transforme le total de Carol en 0 :

-- Input: the customers and orders tables above

SELECT c.name, COALESCE(SUM(o.total), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY total_spent DESC;

-- Output:
-- name  | total_spent
-- ------+------------
-- Bob   | 227.49
-- Alice | 18.00
-- Carol | 0

Parcourir un organigramme du haut vers le bas

Le manager_id de chaque employé pointe vers un autre employé. Une requête récursive part de la personne sans manager et ajoute un niveau de subordonnés à la fois :

CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    manager_id INTEGER,  -- NULL for the person at the top
    FOREIGN KEY (manager_id) REFERENCES employees(id)
);

-- Input: employees
-- id | name  | manager_id
-- ---+-------+-----------
-- 1  | Maya  | NULL
-- 2  | Raj   | 1
-- 3  | Lena  | 1
-- 4  | Tom   | 2
-- 5  | Sofia | 2

-- In SQL Server, write WITH instead of WITH RECURSIVE
WITH RECURSIVE org_chart AS (
    -- Start at the top: the employee with no manager
    SELECT id, name, 0 AS level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- Then add each employee's direct reports, one level at a time
    SELECT e.id, e.name, oc.level + 1
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT name, level
FROM org_chart
ORDER BY level, name;

-- Output:
-- name  | level
-- ------+------
-- Maya  | 0
-- Lena  | 1
-- Raj   | 1
-- Sofia | 2
-- Tom   | 2

Pour aller plus loin sur les relations un-à-plusieurs

Sur ce blog :

Documentation officielle :

Beekeeper Studio est une interface de base de données gratuite & open source

Le meilleur outil de requêtes & éditeur SQL que j’aie jamais utilisé. Il m’offre tout ce dont j’ai besoin pour gérer ma base de données. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio est rapide, intuitif et facile à utiliser. Beekeeper prend en charge plein de bases de données et fonctionne parfaitement sous Windows, Mac et Linux.

La version Linux de Beekeeper est 100% complète : rien de bâclé, aucun compromis sur les fonctionnalités.

Ce que les utilisateurs disent de Beekeeper Studio

★★★★★
« Beekeeper Studio a complètement remplacé mon ancien workflow SQL. C’est rapide, intuitif, et ça rend le travail sur les bases de données à nouveau agréable. »
— Alex K., développeur de bases de données
★★★★★
« J’ai essayé beaucoup d’interfaces de base de données, mais Beekeeper trouve l’équilibre parfait entre fonctionnalités et simplicité. Ça marche, tout simplement. »
— Sarah M., ingénieure full stack