27th Apr 2022 9 minutes de lecture Comment utiliser les sous-requêtes dans les instructions INSERT, UPDATE et DELETE ? Ignacio L. Bisso sql apprendre sql sous-requête Table des matières Point de départ : Un bref rappel des sous-requêtes Première étape : Utilisation de sous-requêtes dans les instructions INSERT Deuxième étape : sous-requêtes dans les instructions UPDATE Troisième étape : Sous-requêtes dans les instructions DELETE Arrivée à la fin de la tournée des sous-requêtes Saviez-vous que les sous-requêtes peuvent également être utilisées dans les instructions UPDATE, INSERT et DELETE ? Les sous-requêtes en SQL sont comme des blocs de construction qui peuvent être utilisés dans une variété d'instructions, pas seulement SELECT. Si vous trouvez les sous-requêtes utiles dans SELECT, lisez cet article pour découvrir à quel point elles peuvent être utiles dans d'autres instructions. Vous serez en mesure de créer des requêtes SQL beaucoup plus complexes et puissantes en un rien de temps ! Si vous souhaitez rafraîchir vos connaissances sur les sous-requêtes, je vous recommande le cours interactif Exercices Pratiques de SQL cours interactif. Il contient plus de 80 exercices sur les sous-requêtes et d'autres constructions difficiles sur SELECT. Point de départ : Un bref rappel des sous-requêtes Commençons par un rappel rapide de ce qu'est une sous-requête. Les sous-requêtes étant le plus souvent utilisées dans les instructions SELECT, nous allons examiner un exemple de sous-requête simple dans une instruction SELECT. Nous pouvons définir une sous-requête comme une requête à l'intérieur d'une autre requête. Si les sous-requêtes sont le plus souvent utilisées dans la clause WHERE des instructions SELECT, elles peuvent être utilisées dans plusieurs autres clauses, notamment WHERE, FROM et HAVING. Voyons la base de données que nous allons utiliser à titre d'exemple. Imaginez que vous êtes le propriétaire d'un magasin de vin, et que vous avez une base de données simple avec 3 tables pour gérer le fonctionnement du magasin. La première table est wineElle contient les produits que vous vendez, avec le nom, le prix, le nombre de bouteilles en stock, etc. pour chaque vin. La seconde est orderqui stocke les commandes que nous recevons de nos clients, y compris le nom du vin commandé et la quantité commandée, entre autres informations. wine NameTypeStockPriceWineCellar BrilliantChardonnay100022SkyWine BleuBlendCabernet98018LeBleu CatedralMalbec10027SantoRojo SantiagoMalbec204024Wines of Chile West SideCabernet140034Napa Wines Oro RossoCabernet75031Italian Caves High CoastChardonnay256017De la Costa wines order Order_idDateClient_idWine_namequantity 1Jan 10 2020100Catedral50 2Feb 15 2020103Santiago230 3Mar 12 2020102West Side85 4Mar 30 2020100Oro Rosso150 5May 3 2020100Oro Rosso30 6Jun 28 2020103Santiago200 7Jun 28 2020102West Side150 Supposons que nous voulions obtenir la liste des vins pour lesquels nous n'avons jamais reçu de commande. La requête ressemblera à ceci : SELECT name, FROM wine WHERE name NOT IN ( SELECT wine_name FROM order ) La sous-requête renvoie les noms de tous les vins pour lesquels nous avons reçu des commandes. Ensuite, la requête externe, en utilisant l'opérateur NOT IN, obtient les noms des vins qui n'ont jamais été inclus dans une commande. Le SQL est aussi simple qu'il est puissant ! Si vous souhaitez revoir certains concepts de sous-requêtes, je vous suggère le cours SQL pour les débutants où vous trouverez une section complète sur les sous-requêtes. Première étape : Utilisation de sous-requêtes dans les instructions INSERT Utilisons maintenant une sous-requête dans une déclaration INSERT. L'idée est d'insérer l'ensemble des résultats d'une sous-requête ou d'une déclaration SELECT dans une table. À titre d'exemple, imaginons que nous voulions créer des factures pour toutes les commandes de vin que nous avons reçues au cours de la journée. Voici une vue partielle de notre invoice table : Invoice_idDateClient_idAmountDescriptionOrder_id 12020-01-10100$13550 bottles of Catedral1 22020-02-15103$5520230 bottles of Santiago2 32020-03-12102$289085 bottles of West Side3 42020-03-30100$4650150 bottles of Oro Rosso4 52020-05-03100$93030 bottles of Oro Rosso5 Supposons que la date d'aujourd'hui soit le 28 juin 2020, et que nous voulions insérer les enregistrements des factures associées aux commandes d'aujourd'hui. Nous pouvons utiliser la requête suivante SELECT pour générer les données des factures : SELECT o.date, o.client_id, o.quantity * w.price as amount, o.quantity || ’ bottles of ‘ || o.wine_name, o.order_id FROM order o JOIN wine w ON w.name = o.wine_name WHERE o.date = ‘2020-06-28’ Le résultat de cette requête est ci-dessous : DateClient_idAmountDescriptionOrder_id 2020-06-28103$4800200 bottles of Santiago6 2020-06-28102$5100150 bottles of West Side7 Voici exactement ce que nous voulons insérer dans la table invoice. Si nous ajoutons simplement une clause INSERT avant la requête, nous pouvons insérer le résultat de la requête dans le tableau. winecomme nous pouvons le voir dans l'exemple suivant : INSERT INTO invoice (date, client_id, amount, wine_name, order_id) SELECT o.date, o.client_id, o.quantity * w.price as amount, o.quantity || ’ bottles of ‘ || o.wine_name, o.order_id FROM order o JOIN wine w ON w.name = o.wine_name WHERE o.date = ‘2020-06-28’ Notez ce qui suit : Nous devons nommer explicitement les colonnes de la table invoice dans laquelle nous effectuons des insertions. Les colonnes de la liste SELECT doivent être dans le même ordre que les colonnes de la table. Nous avons omis la colonne invoice_id pour permettre à la base de données de choisir la valeur suivante à l'aide d'un générateur de séquence par défaut. Après avoir exécuté la clause INSERT, la table invoice contiendra les nouveaux enregistrements de factures pour les commandes du jour. Nous pouvons le voir ci-dessous, avec les nouveaux enregistrements en rouge : Invoice_idDateClient_idAmountDescriptionOrder_id 12020-01-10100$13550 bottles of Catedral1 22020-02-15103$5520230 bottles of Santiago2 32020-03-12102$289085 bottles of West Side3 42020-03-30100$4650150 bottles of Oro Rosso4 52020-05-03100$93030 bottles of Oro Rosso5 62020-06-28103$4800200 bottles of Santiago6 72020-06-28102$5100150 bottles of West Side7 Supposons qu'un jour donné, nous ayons accidentellement exécuté deux fois le programme INSERT et que nos clients aient ainsi reçu deux factures pour chaque commande. Nous ne voulons pas répéter la même erreur à l'avenir ! Pour éviter cela, nous ajoutons une sous-requête à l'instruction INSERT pour vérifier si une facture portant le même nom order_id existe déjà. Voici la nouvelle version de la requête INSERT. La sous-requête ajoutée à la fin identifie les factures qui existent déjà, et la requête externe les élimine en utilisant l'opérateur NOT IN. INSERT INTO invoice (date, client_id, amount, wine_name, order_id) SELECT order.date, order.client_id, o.quantity * w.price as amount, o.quantity || ’ bottles of ‘ || o.wine_name, o.order_id FROM order o JOIN wine ON wine.name = order.wine_name WHERE order.date = ‘2020-06-28’ AND o.order_id NOT IN (SELECT order_id FROM invoice i WHERE i.order_id=o.order_id ) Si vous voulez améliorer vos compétences dans l'utilisation des instructions INSERT, UPDATE, et DELETE, je vous suggère le cours Comment modifier une base de données avec SQL où vous pourrez voir plusieurs exemples de ces instructions. Deuxième étape : sous-requêtes dans les instructions UPDATE Comme SELECT, l'instruction UPDATE peut contenir une sous-requête à plusieurs endroits ou clauses. Dans une déclaration UPDATE, les deux clauses dans lesquelles les sous-requêtes sont le plus souvent utilisées sont SET et WHERE. La clause SET permet de définir la nouvelle valeur de la colonne modifiée par l'instruction UPDATE. Nous pouvons utiliser une sous-requête pour obtenir cette nouvelle valeur, qui peut être sélectionnée dans n'importe quelle table ou n'importe quelle sous-requête valide, à condition de ne renvoyer qu'un seul enregistrement avec une seule colonne pour chaque enregistrement mis à jour. Le type de données de la colonne renvoyée par la sous-requête doit être du même type que celui de la colonne en cours de modification. Créons un UPDATE pour maintenir à jour notre stock de bouteilles de vin. Nous exécuterons un UPDATE à la fin de la journée pour refléter ce que nous avons vendu aujourd'hui. Le code ressemblera à ceci : UPDATE wine w SET stock = stock - ( SELECT SUM (quantity) FROM order WHERE date = CURRENT_DATE AND order.wine_name = w.name ) Si nous n'utilisons pas la clause WHERE dans la requête UPDATE, nous finirons par modifier tous les enregistrements de la table wine, y compris les enregistrements des vins que nous n'avons pas vendus aujourd'hui. La sous-requête renvoie un NULL pour tout vin qui n'a pas été vendu aujourd'hui, et nous aurions par erreur SET la colonne stock à NULL, puisque le résultat de l'expression "stock - NULL" est NULL. Nous devons corriger ce problème. Il existe deux approches pour le corriger. La première consiste à modifier l'expression SUM(quantity) pour qu'elle renvoie un zéro au lieu de NULL. Pour cela, nous devons simplement utiliser la fonction COALESCE, comme ceci : UPDATE wine w SET stock = stock - ( SELECT coalesce(SUM (quantity), 0) FROM order WHERE date = CURRENT_DATE AND order.wine_name = w.name ) La deuxième approche consiste à ajouter une sous-requête dans la clause WHERE pour modifier uniquement les vins qui ont été commandés aujourd'hui et maintenir l'expression SUM(quantity) telle quelle. La requête suivante illustre cette approche : UPDATE wine w SET stock = stock - ( SELECT SUM (quantity) FROM order WHERE date = CURRENT_DATE AND order.wine_name = w.name ) WHERE w.name IN (SELECT order.wine_name FROM order WHERE date = CURRENT_DATE) Ce UPDATE n'est pas idéal : cette clause SET utilise une sous-requête corrélée. Une sous-requête corrélée est une requête que la base de données doit exécuter plusieurs fois - une fois pour chaque ligne modifiée dans la table. Dans notre exemple, la requête obtient l'adresse SUM(quantity) pour chaque vin vendu aujourd'hui. Bien que les sous-requêtes corrélées puissent être puissantes, il est préférable de les éviter autant que possible pour des raisons de bonnes pratiques. Ici, nous pouvons éviter la sous-requête corrélée en utilisant une clause FROM dans la requête UPDATE, comme nous pouvons le voir ci-dessous : UPDATE wine w SET stock = stock - subquery.total_in_orders FROM ( SELECT wine_name, sum( order_quantity ) AS total_in_orders FROM order WHERE date = TODAY GROUP BY wine_name ) subquery WHERE w.name = subquery.wine_name Comme ressource complémentaire, je vous suggère l'article Beginners Guide to the SQL Subquery, où vous pourrez apprendre l'ABC des sous-requêtes à travers des exemples de complexité variable. Troisième étape : Sous-requêtes dans les instructions DELETE Avec l'instruction DELETE, les sous-requêtes peuvent être utilisées uniquement dans une clause WHERE. Disons que nous voulons éliminer les enregistrements des vins pour lesquels nous n'avons reçu aucune commande au cours des 6 derniers mois. Nous pouvons créer une sous-requête qui renvoie les vins vendus au cours des 6 derniers mois, puis identifier les enregistrements que nous voulons supprimer dans la table wine en utilisant l'opérateur NOT IN. Voyons comment SQL procède : DELETE FROM wine w WHERE name NOT IN ( SELECT wine_name FROM order WHERE date >= CURRENT_DATE - interval ‘6 Months’ ) Supposons maintenant que nous voulions éliminer les vins pour lesquels le total des commandes au cours des 6 derniers mois était inférieur à 10 unités. L'instruction DELETE ressemblera à ceci : DELETE FROM wine w WHERE 10 > ( SELECT SUM(quantity) FROM order o WHERE o.wine_name = w.name AND date >= CURRENT_DATE - interval ‘6 Months’ ) Ici, la sous-requête renvoie la quantité de bouteilles commandées au cours des 6 derniers mois pour chaque vin. En comparant cette quantité à 10, nous pouvons déterminer si un vin donné doit être éliminé. Arrivée à la fin de la tournée des sous-requêtes Les sous-requêtes sont comme des blocs de construction en SQL. Nous avons vu comment elles peuvent être utilisées à différents endroits, par exemple dans les instructions SELECT ou dans les instructions de modification telles que INSERT, UPDATE et DELETE. Nous avons vu comment nous pouvions utiliser une sous-requête dans différentes clauses et qu'il existait différentes façons d'utiliser les sous-requêtes dans les instructions INSERT, UPDATE et DELETE. Si vous souhaitez en savoir plus sur les sous-requêtes, je vous suggère l'article Subquery vs. CTE : A SQL Primer, dans lequel un type de sous-requête appelé CTE est expliqué avec de nombreux exemples et détails. Enfin, il existe deux cours contenant de nombreuses informations utiles sur les sous-requêtes, avec de nombreux exemples : SQL pour les débutants où vous trouverez une section complète sur les sous-requêtes, et Comment modifier une base de données avec SQL qui est un cours plus avancé. Allons améliorer vos compétences en SQL ! Tags: sql apprendre sql sous-requête