%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL I/SQL I|Accueil · SQL I]] > > | [[Code et Développement/Développement d'Applications/SQL I/03_ Opérations entre les enregistrements\|← Opérations entre les enregistrements]] | [[Code et Développement/Développement d'Applications/SQL I/05_Comment répondre à une demande\|Comment répondre à une demande →]] | > |:---|---:| %% nav-fin %% [[Code et Développement/Développement d'Applications/SQL I/03_ Opérations entre les enregistrements|Opérations entre les enregistrements]] # Requêtes sur deux tables ## Introduction Jusqu'à présent, nous avons appris à interroger des données au sein d'une seule table. Cependant, la puissance des bases de données relationnelles réside dans leur capacité à organiser les informations en plusieurs tables interconnectées, évitant ainsi la redondance et garantissant l'intégrité des données. Pour exploiter pleinement cette architecture, il devient indispensable de pouvoir combiner des données provenant de différentes tables. Ce chapitre vous introduira aux concepts fondamentaux permettant de lier et d'interroger des informations réparties sur deux tables. Nous explorerons les mécanismes qui sous-tendent ces opérations, en commençant par le concept de produit cartésien, pour ensuite aborder les différentes formes de jointures qui constituent le cœur des requêtes multi-tables en SQL. ### Objectifs du chapitre À la fin de ce chapitre, vous serez capable de : - Comprendre le concept de produit cartésien et son implication. - Utiliser la jointure interne (`INNER JOIN`) pour combiner des données correspondantes. - Utiliser les jointures externes (`LEFT JOIN` et `RIGHT JOIN`) pour inclure des données non correspondantes. - Choisir la jointure appropriée en fonction des besoins de votre requête. ### Pré-requis Pour aborder ce chapitre dans les meilleures conditions, vous devez maîtriser les concepts suivants : - Les 6 mots clés d'une requête sélection (`SELECT`, `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`) et les fonctions d'agrégat. - La structure d'une base de données relationnelle (tables, champs, enregistrements, clés primaires, clés étrangères). --- ## 1. La Nécessité de Combiner les Données Dans un système de gestion de base de données relationnelle (SGBDR), les informations sont organisées en tables distinctes pour des raisons de normalisation. Cette approche permet de : - **Réduire la redondance des données** : Chaque information est stockée une seule fois. - **Améliorer l'intégrité des données** : On ne risque pas d'avoir deux versions différentes d'une même information. - **Faciliter les mises à jour** : Une information (par exemple l'adresse d'un client) ne se modifie qu'à un seul endroit. Cependant, cette séparation signifie que pour obtenir une vue complète de certaines informations, il est souvent nécessaire de regrouper des données provenant de plusieurs tables. > [!example] Exemple de schéma > Imaginons une base de données simple pour une entreprise : > - Table `Clients` : `id_client` (clé primaire), `nom_client`, `ville_client`. > - Table `Commandes` : `id_commande` (clé primaire), `id_client` (clé étrangère vers `Clients`), `date_commande`, `montant_total`. > > Pour savoir quels sont les clients qui ont passé des commandes et le détail de ces commandes, nous avons besoin de combiner ces deux tables. ## 2. Le Produit Cartésien Le produit cartésien est le moyen le plus simple, mais souvent le moins utile directement, de combiner des enregistrements de deux tables. Il sert de base conceptuelle aux autres types de jointures. > [!definition] Définition : Produit Cartésien > Le produit cartésien de deux tables $A$ et $B$ est une opération qui combine chaque enregistrement de la table $A$ avec chaque enregistrement de la table $B$. > Si la table $A$ contient $N_A$ enregistrements et la table $B$ contient $N_B$ enregistrements, le produit cartésien résultant contiendra $N_A \times N_B$ enregistrements. ### 2.1. Syntaxe SQL Il existe deux syntaxes principales pour effectuer un produit cartésien : 1. **Syntaxe implicite** ```sql SELECT * FROM TableA, TableB; ``` 2. **Syntaxe explicite** ```sql SELECT * FROM TableA CROSS JOIN TableB; ``` La syntaxe `CROSS JOIN` a l'avantage d'indiquer clairement qu'il s'agit d'un produit cartésien. >[!tip] Nous utiliserons la forme implicite >Afin de nous faciliter les choses, nous choisissons d'utiliser la forme implicite dans le cours et les exercices. >Nous vous laissons libre de choisir la forme qui vous convient le mieux. ### 2.2. Explication et Illustration Considérons nos tables `Clients` et `Commandes` : **Table `Clients`** | id_client | nom_client | ville_client | |-----------|------------|--------------| | 1 | Alice | Paris | | 2 | Bob | Lyon | **Table `Commandes`** | id_commande | id_client | date_commande | montant_total | |-------------|-----------|---------------|---------------| | 101 | 1 | 2023-01-15 | 150.00 | | 102 | 2 | 2023-01-16 | 200.00 | | 103 | 1 | 2023-01-17 | 50.00 | Si nous effectuons un produit cartésien : ```sql SELECT * FROM Clients, Commandes; ``` Le résultat serait : | id_client | nom_client | ville_client | id_commande | id_client | date_commande | montant_total | |-----------|------------|--------------|-------------|-----------|---------------|---------------| | 1 | Alice | Paris | 101 | 1 | 2023-01-15 | 150.00 | | 1 | Alice | Paris | 102 | 2 | 2023-01-16 | 200.00 | | 1 | Alice | Paris | 103 | 1 | 2023-01-17 | 50.00 | | 2 | Bob | Lyon | 101 | 1 | 2023-01-15 | 150.00 | | 2 | Bob | Lyon | 102 | 2 | 2023-01-16 | 200.00 | | 2 | Bob | Lyon | 103 | 1 | 2023-01-17 | 50.00 | > [!note] Utilité du Produit Cartésien > À notre niveau, le produit cartésien est assez rare. Cependant, il a son intérêt quand on veut créer toutes les combinaisons possibles entre deux tables. > > Exemple : un magasin vend des T-shirts en 3 tailles (S, M, L) et en 2 couleurs (blanc, noir). Le produit cartésien de la table des tailles et de la table des couleurs donne les $3 \times 2 = 6$ modèles possibles : S blanc, S noir, M blanc, M noir, L blanc, L noir. > [!warning] Que représente un enregistrement > Depuis le début, nous vous disons qu'il est important de savoir à quoi correspond un enregistrement présent à la sortie du `FROM`. > Avec un produit cartésien, un enregistrement ne correspond ni à un enregistrement de la table A, ni à un enregistrement de la table B mais à une combinaison des deux. > > Dans l'exemple ci-dessus, un enregistrement, en sortie du `FROM`, correspond à une combinaison client/commande. ## 3. Les Jointures (JOIN) Les jointures sont des opérations SQL qui permettent de combiner des enregistrements de deux (ou plusieurs) tables en fonction de champs de relation communs entre elles. C'est le moyen standard et le plus efficace d'interroger des données réparties sur plusieurs tables. > [!definition] Définition : Jointure > Une jointure est une clause SQL qui combine des enregistrements de deux (ou plusieurs) tables en se basant sur un champ qui les relie. La condition de jointure spécifie comment les enregistrements des tables doivent être appariés. ### 3.1. La Condition de Jointure (`ON`) La clause `ON` est essentielle pour toutes les jointures (sauf `CROSS JOIN`). Elle spécifie la condition qui doit être remplie pour que des enregistrements des deux tables soient combinés. >[!info] Nous utiliserons uniquement les clés primaires et les clés étrangères >Les conditions peuvent se faire sur n'importe quel champ des deux tables avec une égalité ou une inégalité. >Généralement, cette condition compare, via une égalité, une clé primaire d'une table à une clé étrangère de l'autre table. > >Dans le niveau débutant et intermédiaire, nous nous limiterons à ce type de conditions. > ```sql SELECT C.nom_client, Co.date_commande FROM Clients C INNER JOIN Commandes Co ON C.id_client = Co.id_client; ``` > [!tip] Rappel : Alias de table > Il est fortement recommandé d'utiliser des alias de table (par exemple `Clients C` ou `Clients AS C`) pour rendre les requêtes plus concises et plus lisibles surtout lorsqu'on utilise des champs de même nom (comme `id_client` dans notre exemple). > [!warning] Que représente un enregistrement > Lorsque nous ferons des jointures entre deux tables, nous le ferons en utilisant comme condition l'égalité entre une clé primaire et sa clé étrangère. > Cela permet de dire que la table principale sera celle où se trouve la clé étrangère et donc qu'un enregistrement en sortie du `FROM` correspondra à cette table. > La table contenant la clé primaire permet de donner des informations complémentaires sur l'objet manipulé. > > Dans l'exemple ci-dessus, un enregistrement, en sortie du `FROM`, correspond à une commande. L'autre table permet de donner des informations sur le client qui passe la commande (son nom et la ville où il habite). ## 4. La Jointure Interne (INNER JOIN) La jointure interne est le type de jointure le plus courant. Elle retourne uniquement les enregistrements lorsque la condition de jointure est satisfaite dans **les deux tables**. Nous verrons qu'elle correspond à un produit cartésien dont on ne garde que certaines lignes (voir [[#4.3. Le lien avec le produit cartésien]]). > [!definition] Définition : INNER JOIN > Une `INNER JOIN` retourne uniquement les enregistrements où il y a une correspondance de valeurs dans les champs spécifiés par la condition de jointure dans *les deux* tables. Les enregistrements sans correspondance dans l'une ou l'autre table sont exclus du résultat. >[!tip] jointure symétrique >Pour les jointures `INNER JOIN`, nous parlerons aussi de **jointure symétrique**. ### 4.1. Syntaxe SQL ```sql SELECT champs_désirés FROM TableA INNER JOIN TableB ON TableA.champ_commun = TableB.champ_commun; ``` Le mot-clé `INNER` est optionnel ; `JOIN` seul est interprété comme `INNER JOIN` par défaut. Cependant, c'est une bonne pratique de l'inclure pour la clarté. ### 4.2. Explication et Illustration Reprenons nos tables `Clients` et `Commandes`, auxquelles nous ajoutons un client qui n'a passé aucune commande (`Charlie`) et une commande dont le client n'a pas été renseigné (la commande `104`, dont le champ `id_client` est vide, c'est-à-dire `NULL`). Nous voulons voir les noms des clients et les détails de leurs commandes. ```sql SELECT C.nom_client, C.ville_client, Co.id_commande, Co.date_commande, Co.montant_total FROM Clients C INNER JOIN Commandes Co ON C.id_client = Co.id_client; ``` **Table `Clients`** | id_client | nom_client | ville_client | | --------- | ---------- | ------------ | | 1 | Alice | Paris | | 2 | Bob | Lyon | | 3 | Charlie | Marseille | **Table `Commandes`** | id_commande | id_client | date_commande | montant_total | |-------------|-----------|---------------|---------------| | 101 | 1 | 2023-01-15 | 150.00 | | 102 | 2 | 2023-01-16 | 200.00 | | 103 | 1 | 2023-01-17 | 50.00 | | 104 | NULL | 2023-01-18 | 300.00 | Le résultat de l'`INNER JOIN` serait : | nom_client | ville_client | id_commande | date_commande | montant_total | |------------|--------------|-------------|---------------|---------------| | Alice | Paris | 101 | 2023-01-15 | 150.00 | | Alice | Paris | 103 | 2023-01-17 | 50.00 | | Bob | Lyon | 102 | 2023-01-16 | 200.00 | > [!note] Observations > - Le client `Charlie` (id_client = 3) n'apparaît pas car il n'a pas de commande correspondante dans la table `Commandes`. > - La commande `104` (id_client = `NULL`) n'apparaît pas car il n'y a pas de client correspondant dans la table `Clients`. > > L'`INNER JOIN` est donc utile lorsque vous voulez uniquement les données qui ont une correspondance exacte dans *toutes* les tables jointes. > [!warning] Que représente un enregistrement > Nous avons déjà dit que la table principale sera celle où se trouve la clé étrangère et donc qu'un enregistrement en sortie du `FROM` correspondra à cette table. > > L'`INNER JOIN` donne une information supplémentaire. En effet, il impose une égalité stricte entre la clé primaire et la clé étrangère : l'enregistrement sortant du `FROM` n'existera que s'il y a un enregistrement dans chaque table. > On en conclut qu'un enregistrement qui sort du `FROM` correspond à un enregistrement de la table contenant la clé étrangère dont on connaît l'enregistrement de l'autre table (celle contenant la clé primaire). > > Dans l'exemple ci-dessus, un enregistrement, en sortie du `FROM`, correspond à **une commande dont on connaît le client**. ### 4.3. Le lien avec le produit cartésien Une `INNER JOIN` peut se voir comme un **produit cartésien dont on ne garde que les lignes qui respectent la condition du `ON`**. Reprenons le produit cartésien de la section 2.2. Les lignes où le `id_client` du client est différent du `id_client` de la commande ne respectent pas la condition `C.id_client = Co.id_client`. Elles sont mises en rouge ci-dessous et vont disparaître : | id_client | nom_client | ville_client | id_commande | id_client | date_commande | montant_total | |-----------|------------|--------------|-------------|-----------|---------------|---------------| | 1 | Alice | Paris | 101 | 1 | 2023-01-15 | 150.00 | | <em style="color:red;text-decoration: line-through;">1</em> | <em style="color:red;text-decoration: line-through;">Alice</em> | <em style="color:red;text-decoration: line-through;">Paris</em> | <em style="color:red;text-decoration: line-through;">102</em> | <em style="color:red;text-decoration: line-through;">2</em> | <em style="color:red;text-decoration: line-through;">2023-01-16</em> | <em style="color:red;text-decoration: line-through;">200.00</em> | | 1 | Alice | Paris | 103 | 1 | 2023-01-17 | 50.00 | | <em style="color:red;text-decoration: line-through;">2</em> | <em style="color:red;text-decoration: line-through;">Bob</em> | <em style="color:red;text-decoration: line-through;">Lyon</em> | <em style="color:red;text-decoration: line-through;">101</em> | <em style="color:red;text-decoration: line-through;">1</em> | <em style="color:red;text-decoration: line-through;">2023-01-15</em> | <em style="color:red;text-decoration: line-through;">150.00</em> | | 2 | Bob | Lyon | 102 | 2 | 2023-01-16 | 200.00 | | <em style="color:red;text-decoration: line-through;">2</em> | <em style="color:red;text-decoration: line-through;">Bob</em> | <em style="color:red;text-decoration: line-through;">Lyon</em> | <em style="color:red;text-decoration: line-through;">103</em> | <em style="color:red;text-decoration: line-through;">1</em> | <em style="color:red;text-decoration: line-through;">2023-01-17</em> | <em style="color:red;text-decoration: line-through;">50.00</em> | Il reste : | id_client | nom_client | ville_client | id_commande | id_client | date_commande | montant_total | |-----------|------------|--------------|-------------|-----------|---------------|---------------| | 1 | Alice | Paris | 101 | 1 | 2023-01-15 | 150.00 | | 1 | Alice | Paris | 103 | 1 | 2023-01-17 | 50.00 | | 2 | Bob | Lyon | 102 | 2 | 2023-01-16 | 200.00 | Seules les combinaisons qui ont un sens sont conservées : chaque commande est associée à **son** client. On retrouve exactement le résultat de l'`INNER JOIN` de la section 4.2. ## 5. Les Jointures Externes (OUTER JOINs) Contrairement à l'`INNER JOIN` qui ne retourne que les correspondances exactes, les jointures externes permettent d'inclure les enregistrements qui n'ont pas de correspondance dans l'une des tables. Nous étudierons deux jointures externes : `LEFT JOIN` et `RIGHT JOIN`. > [!note] Valeurs `NULL` > Lorsqu'une jointure externe ne trouve pas de correspondance pour un enregistrement d'une table, les champs de la table non correspondante sont remplis avec des valeurs `NULL`. >[!tip] jointure dissymétrique > Pour les jointures `LEFT JOIN` et `RIGHT JOIN` nous parlerons aussi de **jointure dissymétrique**. ### 5.1. La Jointure Gauche (LEFT JOIN / LEFT OUTER JOIN) > [!definition] Définition : LEFT JOIN > Une jointure `LEFT JOIN` retourne tous les enregistrements de la table de gauche, et les enregistrements correspondants de la table de droite. Si un enregistrement de la table de gauche n'a pas de correspondance dans la table de droite, les champs de la table de droite pour cet enregistrement seront `NULL`. #### 5.1.1. Syntaxe SQL ```sql SELECT champs_désirés FROM TableA LEFT JOIN TableB ON TableA.champ_commun = TableB.champ_commun; ``` Le mot-clé `OUTER` est optionnel. `LEFT JOIN` est suffisant. #### 5.1.2. Explication et Illustration Utilisons nos tables `Clients` et `Commandes` : ```sql SELECT C.nom_client, C.ville_client, Co.id_commande, Co.date_commande, Co.montant_total FROM Clients C LEFT JOIN Commandes Co ON C.id_client = Co.id_client; ``` **Table `Clients`** | id_client | nom_client | ville_client | |-----------|------------|--------------| | 1 | Alice | Paris | | 2 | Bob | Lyon | | 3 | Charlie | Marseille | **Table `Commandes`** | id_commande | id_client | date_commande | montant_total | |-------------|-----------|---------------|---------------| | 101 | 1 | 2023-01-15 | 150.00 | | 102 | 2 | 2023-01-16 | 200.00 | | 103 | 1 | 2023-01-17 | 50.00 | | 104 | NULL | 2023-01-18 | 300.00 | Le résultat de la `LEFT JOIN` serait : | nom_client | ville_client | id_commande | date_commande | montant_total | |------------|--------------|-------------|---------------|---------------| | Alice | Paris | 101 | 2023-01-15 | 150.00 | | Alice | Paris | 103 | 2023-01-17 | 50.00 | | Bob | Lyon | 102 | 2023-01-16 | 200.00 | | Charlie | Marseille | NULL | NULL | NULL | > [!note] Observations > - Tous les clients (`Alice`, `Bob`, `Charlie`) sont inclus dans le résultat, car `Clients` est la table de gauche. > - Pour `Charlie`, qui n'a pas de commande, les champs de la table `Commandes` sont `NULL`. > - La commande `104` (d'un client inconnu) n'apparaît pas, car elle n'a pas de correspondance dans la table de gauche (`Clients`). > [!tip] Cas d'utilisation courant (à connaître !) > La `LEFT JOIN` est très utile pour trouver les enregistrements d'une table qui n'ont *pas* de correspondance dans une autre table. > Par exemple, pour trouver tous les clients qui n'ont jamais passé de commande : > ```sql > SELECT C.nom_client > FROM Clients C LEFT JOIN Commandes Co ON C.id_client = Co.id_client > WHERE Co.id_commande IS NULL; -- On filtre les enregistrements où la partie droite est NULL > ``` > Le résultat serait : > | nom_client | > |------------| > | Charlie | > > Pour savoir si la partie droite est vide, on teste un champ de la table de droite qui est **toujours rempli** quand l'enregistrement existe : sa clé primaire (ici `id_commande`). C'est la méthode utilisée dans l'atelier 1 (question 06). ### 5.2. La Jointure Droite (RIGHT JOIN / RIGHT OUTER JOIN) > [!definition] Définition : RIGHT JOIN > Une jointure `RIGHT JOIN` retourne tous les enregistrements de la table de droite, et les enregistrements correspondants de la table de gauche. Si un enregistrement de la table de droite n'a pas de correspondance dans la table de gauche, les champs de la table de gauche pour cet enregistrement seront `NULL`. #### 5.2.1. Syntaxe SQL ```sql SELECT champs_désirés FROM TableA RIGHT JOIN TableB ON TableA.champ_commun = TableB.champ_commun; ``` Le mot-clé `OUTER` est optionnel. `RIGHT JOIN` est suffisant. #### 5.2.2. Explication et Illustration Reprenons nos tables `Clients` et `Commandes` : ```sql SELECT C.nom_client, C.ville_client, Co.id_commande, Co.date_commande, Co.montant_total FROM Clients C RIGHT JOIN Commandes Co ON C.id_client = Co.id_client; ``` **Table `Clients`** | id_client | nom_client | ville_client | |-----------|------------|--------------| | 1 | Alice | Paris | | 2 | Bob | Lyon | | 3 | Charlie | Marseille | **Table `Commandes`** | id_commande | id_client | date_commande | montant_total | |-------------|-----------|---------------|---------------| | 101 | 1 | 2023-01-15 | 150.00 | | 102 | 2 | 2023-01-16 | 200.00 | | 103 | 1 | 2023-01-17 | 50.00 | | 104 | NULL | 2023-01-18 | 300.00 | Le résultat de la `RIGHT JOIN` serait : | nom_client | ville_client | id_commande | date_commande | montant_total | |------------|--------------|-------------|---------------|---------------| | Alice | Paris | 101 | 2023-01-15 | 150.00 | | Alice | Paris | 103 | 2023-01-17 | 50.00 | | Bob | Lyon | 102 | 2023-01-16 | 200.00 | | NULL | NULL | 104 | 2023-01-18 | 300.00 | > [!note] Observations > - Toutes les commandes (`101`, `102`, `103`, `104`) sont incluses dans le résultat, car `Commandes` est la table de droite. > - Pour la commande `104`, qui n'a pas de client correspondant, les champs de la table `Clients` sont `NULL`. > - Le client `Charlie` (sans commande) n'apparaît pas, car il n'a pas de correspondance dans la table de droite (`Commandes`). > [!tip] Équivalence `LEFT JOIN` / `RIGHT JOIN` > Toute jointure `RIGHT JOIN` peut être réécrite en jointure `LEFT JOIN` en inversant l'ordre des tables. Par exemple, la requête précédente est équivalente à : > ```sql > SELECT > C.nom_client, > C.ville_client, > Co.id_commande, > Co.date_commande, > Co.montant_total > FROM Commandes Co LEFT JOIN Clients C ON C.id_client = Co.id_client; > ``` > > La `LEFT JOIN` est souvent plus utilisée que la `RIGHT JOIN`, car, pour améliorer la lisibilité de la requête, nous avons tendance à commencer par la table que nous considérons comme la plus importante. > [!warning] Que représente un enregistrement > Nous avons déjà dit que la table principale sera celle où se trouve la clé étrangère et donc qu'un enregistrement en sortie du `FROM` correspondra à cette table. > > En fonction de la jointure externe vous pourrez être plus précis et donner une des affirmations suivantes : > > 1- Si votre jointure prend tous les enregistrements de la table où se trouve la clé étrangère, un enregistrement qui sort du `FROM` correspond à un enregistrement de cette table, dont on connaît ou non l'enregistrement associé dans l'autre table. > Exemple : > ```sql > FROM Commandes Co LEFT JOIN Clients C ON Co.id_client = C.id_client > ``` > Dans l'exemple ci-dessus, un enregistrement, en sortie du `FROM`, correspond à **une commande, dont on connaît ou non le client**. > > 2- Si votre jointure prend tous les enregistrements de la table où se trouve la clé primaire, un enregistrement qui sort du `FROM` correspond : > - soit à un enregistrement de la table contenant la clé étrangère, avec son enregistrement associé ; > - soit à un enregistrement de la table contenant la clé primaire qui n'a aucun enregistrement associé. > > Exemple : > ```sql > FROM Commandes Co RIGHT JOIN Clients C ON Co.id_client = C.id_client > ``` > Dans l'exemple ci-dessus, un enregistrement, en sortie du `FROM`, correspond **soit à une commande avec son client, soit à un client sans commande**. ## 6. Récapitulatif : quelle jointure choisir ? | Jointure | Enregistrements gardés | Enregistrements perdus | Exemple de question (avec nos tables) | |----------|------------------------|------------------------|---------------------------------------| | `Clients INNER JOIN Commandes` | Uniquement ceux qui ont une correspondance dans les deux tables | Ceux sans correspondance, des deux côtés | « La liste des commandes avec le nom de leur client » | | `Clients LEFT JOIN Commandes` | Tous ceux de la table de gauche (`Clients`) | Ceux de la table de droite sans correspondance | « Tous les clients, y compris ceux qui n'ont jamais commandé » | | `Clients RIGHT JOIN Commandes` | Tous ceux de la table de droite (`Commandes`) | Ceux de la table de gauche sans correspondance | « Toutes les commandes, y compris celles dont on ne connaît pas le client » | > [!tip] La question à se poser > **De quelle table dois-je garder *tous* les enregistrements ?** > - Aucune : `INNER JOIN` > - Celle de gauche : `LEFT JOIN` > - Celle de droite : `RIGHT JOIN` --- Nous allons voir ensuite comment se préparer à répondre à une demande : [[05_Comment répondre à une demande|Comment répondre à une demande]] %% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL I/SQL I|Accueil · SQL I]] > > | [[Code et Développement/Développement d'Applications/SQL I/03_ Opérations entre les enregistrements\|← Opérations entre les enregistrements]] | [[Code et Développement/Développement d'Applications/SQL I/05_Comment répondre à une demande\|Comment répondre à une demande →]] | > |:---|---:| %% nav-fin %% --- 🗓️ Historique Dernière mise à jour : `21 octobre 2026` Rédigé par : [[Vincent TELLIER]]