Les Jointures - Louis SWINNEN
Transcription
Les Jointures - Louis SWINNEN
Département Informatique Laboratoires de bases de données Laboratoire n°3 Les Jointures par Danièle BAYERS et Louis SWINNEN Ce document est disponible sous licence Creative Commons indiquant qu’il peut être reproduit, distribué et communiqué pour autant que le nom des auteurs reste présent, qu’aucune utilisation commerciale ne soit faite à partir de celui-ci et que le document ne soit ni modifié, ni transformé, ni adapté. http://creativecommons.org/licenses/by-nc-nd/2.0/be/ La Haute Ecole Libre Mosane (HELMo) attache une grande importance au respect des droits d’auteur. C’est la raison pour laquelle nous invitons les auteurs dont une oeuvre aurait été, malgré tous nos efforts, reproduite sans autorisation suffisante, à contacter immédiatement le service juridique de la Haute Ecole afin de pouvoir régulariser la situation au mieux. Décembre 2010 1. Objectif L’objectif de ce laboratoire est d’aborder le concept de jointure dans son ensemble en commençant par le produit cartésien, ensuite nous abordons les jointures naturelles et conditionnelles. Enfin nous terminons par les opérations de groupe, également très importante dans l’exploitation des bases de données. 2. La base de données Dans les exemples, nous supposerons que nous disposons de la base de données suivante : Produit NumProduit Libelle Prix QteStock id: NumProduit acc LignesCommande NumCommande NumProduit Qte id: NumProduit NumCommande acc ref: NumProduit ref: NumCommande acc Commande NumCommande NumClient DateCommande id: NumCommande acc ref: NumClient acc Client NumClient Nom Adresse Localite Categorie Compte id: NumClient acc 3. La jointure La jointure permet l’utilisation de plusieurs tables dans une requête SQL. Mathématiquement, la jointure se définit comme un sous-ensemble du produit cartésien des relations (i.e. tables) listées. La jointure peut être de type différent. On parle ainsi de produit cartésien, de jointure naturelle ou encore de jointure conditionnelle. 3.1 Le produit cartésien Le cross join peut se représenter comme le produit cartésien complet entre les différentes tables. Tous les éléments sont donc repris dans le résultat. Format : SELECT attr1, attr2, …, attrn FROM table1 CROSS JOIN table2 L’utilisation de CROSS JOIN est très limitée puisque le résultat obtenu n’est pas nécessairement significatif. Le SGBD créant des relations n’ayant pas nécessairement de sens. Exemple : SELECT * FROM client CROSS JOIN commande ORDER BY 1 2 Le résultat sera composé de l’ensemble des paires client – commande. Ainsi, dans ce résultat, chaque client sera associé à chaque commande. Le nombre de lignes dans le résultat sera donné par : #client x #commande 3.2 Les jointures naturelles La jointure naturelle est une forme un peu particulière de jointure. En effet, en fonction des noms des attributs, le SGBD va automatiquement réaliser les liens entre les tables. Ainsi, lorsque les attributs présents dans les tables sélectionnées portent un nom identique, le SGBD considère qu’il s’agit de tables liées et construit le résultat en fonction de ces liens ainsi découverts. On préfère toujours utiliser des jointures plus explicites. Les jointures naturelles peuvent, en cas d’évolution de la structure de la base de données, occasionner des dysfonctionnements dans des applications déjà existantes. Format : SELECT attr1, attr2, …, attrn FROM table1 NATURAL JOIN table2 Exemple : SELECT * FROM client NATURAL JOIN commande Sélectionne les clients ayant déjà passé au moins une commande. Le SGBD liera ici automatiquement les tables client et commande sur base du numClient présent dans chacune d’entre-elle. 3.3 Les jointures conditionnelles Dans cette forme de jointure, nous allons exprimer une condition d’égalité permettant d’obtenir des enregistrements liés entre eux. En effet, en exploitant les clés étrangères liant les tables, nous obtenons des résultats exploitables. Exemple : obtenir le numéro et le libellé des produits ayant été commandés le 10/10/08. Pour ce faire, il faut partir de la table produit, par les clés étrangères, obtenir les lignes de commandes reprenant ces produits et enfin les commandes liées à ces lignes de commande. Il reste à limiter le résultat à la date demandée. Avec des requêtes ne portant que sur une seule table, il est impossible d’obtenir ce genre de résultats. Il est, par contre, très simple, d’écrire une seule requête donnant les produits demandés. Format de la jointure (SQL-89) : SELECT attribut1, attribut2, …, attributm FROM table1, table2, …, tablen WHERE condition1 AND condition2 AND … AND conditionn-1 Cette forme doit être abandonnée ! Format de la jointure (SQL-92) : SELECT attribut1, attribut2, …, attributm FROM table1 JOIN table2 ON condition1 … 3 JOIN tablen ON conditionn-1 La jointure permet d’écrire des requêtes impliquant plusieurs tables. Il est nécessaire lorsqu’on mentionne plusieurs tables dans une requête SQL d’inclure les conditions de jointure. Ces conditions expriment les liens existants entre les tables (très souvent au travers des clés étrangères). Sur base de cette condition, le SGBD ne va garder que certains résultats jugés conformes par le programmeur. Ainsi, la requête qui correspond à la demande exprimée plus haut peut s’écrire : SELECT numProduit, libelle FROM produit p JOIN LignesCommande lc ON p.numProduit = lc.numProduit JOIN Commande c ON c.numCommande = lc.numCommande WHERE c.dateCommande = TO_DATE('10102008', 'DDMMYYYY') ; À l’exécution de cette requête, on peut imaginer que le SGBD va créer tous les triplets possibles entre les tables produit, LignesCommande et commande (le produit cartésien de ces 3 tables). Une fois l’ensemble des résultats obtenus, le SGBD supprime les lignes qui ne satisfont pas aux conditions de jointure (égalité sur le numéro du produit entre la table produit et la table lignesCommande, égalité sur le numéro de commande entre la table lignesCommande et la table commande). Enfin, le SGBD supprime les enregistrements ne satisfaisant pas la condition WHERE. Autres exemples : Sélectionner les clients ayant commandés le produit numéro 123. Dans cette condition, on exprime plusieurs liens entre des tables. Il faut sélectionner des clients ayant passés des commandes. Il faut, dans les commandes passées, garder uniquement celles dont au moins une ligne fait référence au produit 123. SELECT c.numClient FROM Commande c JOIN LignesCommande lc ON WHERE lc.numProduit = 123 c.numCommande = lc.numCommande Si nous souhaitons afficher les informations de ce client particulier, il est nécessaire de faire une nouvelle jointure avec la table client. Ainsi, la requête s’exprimerait comme suit : SELECT cli.nomClient, cli.Adresse, cli.Localite FROM client cli JOIN Commande c ON cli.numClient = c.numClient JOIN LignesCommande lc ON c.numCommande = lc.numCommande WHERE lc.numProduit = 123 Les jointures font parties des éléments très importants dans les bases de données. Grâce à elles, il est possible d’exploiter de grands dépôts de données en obtenant uniquement les résultats souhaités. 3.4 Les auto-jointures Cette forme de jointure est parfois utilisée. Elle consiste à écrire une requête contenant une jointure désignant la même table (dans le JOIN) que celle de la requête elle-même (dans le FROM). 4 Cette forme particulière impose de nommer les tables afin de pouvoir exprimer les conditions sans ambiguïté. La forme de cette jointure devient alors : SELECT attr1, …, attrn FROM table t1 JOIN table t2 ON t1.attrx = t2.attrx WHERE t1.attry … [ORDER BY …] Dans cette requête, t1 désigne la table de la requête tandis que t2 désigne la table de la jointure. Exemple : SELECT c2.numCommande, c2.dateCommande FROM commande c1 JOIN commande c2 ON c2.dateCommande > c1.dateCommande WHERE c1.numCommande = 'C001' Sélectionne les commandes dont la date de la commande est postérieure à celle de la commande C001. 3.5 Les jointures externes Nous avons vu que les jointures permettaient, par la condition de jointure, de lier des tables entre-elles pour obtenir les enregistrements souhaités. Il est parfois important d’obtenir aussi les enregistrements pour lesquels il n’existe pas de correspondance dans l’autre table. Dans ces cas, les jointures externes sont utilisées. Format : SELECT attr1, …, attrn FROM table1 t1 [LEFT OUTER|RIGHT OUTER] JOIN table2 t2 ON t1.attrx = t2.attry WHERE t1.attry … [ORDER BY …] Dans cette forme, la jointure est réalisée entre la table t1 et la table t2. En plus des résultats habituels suite à la jointure, nous obtenons les enregistrements n’ayant pas de correspondance. Dans le cas d’un LEFT OUTER, les enregistrements de la table t1 n’ayant pas de correspondance (par la condition de jointure) dans la table t2 sont inclus également dans le résultat. Dans le cas d’un RIGHT OUTER, les enregistrements de la table t2 n’ayant pas de correspondance (par la condition de jointure) dans la table t1 sont inclus dans le résultat. Exemple (extrait de [1]) : SELECT DISTINCT cli.numClient, cli.nom, com.dateCommande FROM Client cli LEFT OUTER JOIN Commande com ON cli.numClient = com.numClient Dans cet exemple, on affiche les clients et leur date de commande. Si un client n’a pas commandé, il sera inclus dans le résultat mais sa date de commande vaudra NULL. 5 En plus de LEFT OUTER et RIGHT OUTER, SQL-2 définit FULL OUTER qui combine les deux options (LEFT OUTER et RIGHT OUTER) en même temps. 4. Requêtes sur les groupes Grâce aux requêtes sur les groupes, il est possible d’obtenir des résultats groupés qui peuvent s’avérer intéressants. Ces requêtes sur les groupes utilisent des fonctions de groupe. 4.1 Les fonctions de groupe Le langage SQL prévoit les fonctions de groupe suivantes applicables à un attribut : • AVG – calcule la moyenne • MAX – Retourne la plus grande valeur • MIN – Retourne la plus petite valeur • COUNT – Compte le nombre de résultat • SUM – Effectue une sommation Ces fonctions de groupe prennent en argument le nom de l’attribut. Elles peuvent également intégrer le mot clé DISTINCT qui supprime les valeurs identiques. Exemples : SELECT AVG(prix) FROM produit Obtenir le prix moyen des produits Pour clarifier l’utilisation de COUNT, voici quelques exemples en guise d’illustration : SELECT * FROM client Retourne tous les enregistrements de la table client (n lignes) SELECT COUNT(*) FROM client Retourne le nombre d’enregistrements présents dans la table client (i.e. le nombre de lignes du résultat précédent). SELECT COUNT(categorie) FROM client Retourne le nombre d’enregistrements présents dans la table client pour lesquels la catégorie n’est pas nulle. SELECT COUNT(DISTINCT categorie) FROM client Retourne le nombre de valeurs différentes pour la catégorie. 4.2 Les clauses GROUP BY et HAVING La clause GROUP BY permet de regrouper les résultats autour de valeurs d’attribut déterminées. Cette clause utilise très souvent des fonctions de groupe. Forme : 6 SELECT attr1, …, attri, fct_groupe1(attrj), …, fct_groupen(attrp) FROM table1 [JOIN tablen ON conditionx] [WHERE conditiony] GROUP BY attr1, …, attri [HAVING conditionz] Dans la forme présentée ci-dessus, il faut remarquer ceci : • L’utilisation conjointe d’attributs et de fonctions de groupe dans le SELECT impose l’utilisation d’une clause GROUP BY • Le GROUP BY doit souvent reprendre les attributs n’intégrant pas les fonctions de groupe • La clause WHERE ne peut reprendre dans sa condition que des attributs • La clause HAVING permet de filtrer les résultats d’une fonction de groupe • Dans fct_groupe, on retrouve les fonctions de groupe montrées plus haut : SUM, MAX, MIN, AVG et COUNT. Exemple : SELECT cli.localite, SUM(p.prix * lc.qte) CA FROM client cli JOIN commande c ON cli.numClient = c.numClient JOIN lignesCommande lc ON lc.numCommande = c.numCommande JOIN produit p ON lc.numProduit = p.numProduit GROUP BY cli.localite Cette requête retourne comme résultat le chiffre d’affaires par localité. Le résultat montrera les localités différentes et le chiffre d’affaires calculé. La clause HAVING peut être définie comme suit : « il s’agit d’une clause WHERE sur un groupe définit par GROUP BY » (définition extraite de [1]). Tout comme la clause WHERE, la clause HAVING limite le résultat aux seuls enregistrements vérifiant la condition énoncée. Exemple : SELECT cli.categorie, SUM(p.prix * lc.qte) CA FROM client cli JOIN commande c ON cli.numClient = c.numClient JOIN lignesCommande lc ON lc.numCommande = c.numCommande JOIN produit p ON lc.numProduit = p.numProduit WHERE cli.localite IN ('Liege', 'Bruxelles') GROUP BY cli.categorie HAVING SUM(p.prix * lc.qte) > 500 Dans cet exemple, on calcule le chiffre d’affaires par catégorie de client, uniquement pour les clients de Liège et de Bruxelles. On filtre le résultat pour garder uniquement les catégories dont le chiffre d’affaires est supérieur à 500. On remarque dans cet exemple l’utilisation différente de WHERE et HAVING. WHERE est utilisé pour filtrer les catégories, attribut de la table client tandis que HAVING est utilisé pour filtrer le résultat d’une fonction de groupe, ici SUM. 7 5. Stratégie pour l’écriture d’une jointure Dans [1], l’auteur décrit la stratégie suivante pour l’écriture d’une jointure : 1. « Déterminer les tables à mettre en jeu, les inclure dans la clause FROM. 2. Inclure les attributs à visualiser dans la clause SELECT 3. Si le groupe SELECT comporte des fonctions sur les groupes, il faut une clause GROUP BY reprenant tous les attributs cités dans la clause SELECT, sauf les arguments des fonctions en question 4. Déterminer les conditions limitant la recherche. Les conditions portant sur les groupes doivent figurer dans une clause HAVING, celles portant sur des valeurs individuelles dans une clause WHERE 5. Pour employer une fonction sur les groupes dans une clause WHERE, ou si on a besoin de la valeur d’un attribut d’une autre table, il est nécessaire d’utiliser une requête imbriquée 6. Pour fusionner les résultats venant de deux clauses SELECT, il suffit de les joindre par un UNION 7. Préciser l’ordre d’apparition des tuples du résultat dans une clause ORDER BY. » Stratégie pour l’écriture d’une jointure (extrait de [1]) Exercices 1. Afficher le titre et l’éditeur de chaque livre 2. Trouver le nom, prénom et l’adresse des emprunteurs qui n’ont pas rendu un exemplaire 3. Afficher les noms et prénoms des emprunteurs dont la cotisation est inférieure à la cotisation de base selon la catégorie de l’emprunteur 4. Trouver les titres et les dates de publication des livres écrits par Cook ou Meyer. Ordonner les titres par date de publication 5. Afficher pour chaque emprunteur de Liège et de Spa, les titres des livres empruntés, l’éditeur, la catégorie, la date d’emprunt et l’état de l’exemplaire 6. Afficher le nombre de livres publiés par année (de publication) 7. Afficher le nombre de livres par nom de catégorie 8. Compter le nombre de livres écrit par chaque auteur. Afficher le résultat par ordre alphabétique inverse des noms d’auteur 9. Quelle est la cote minimale et la cote maximale des livres ? 10. Pour chaque livre, afficher le titre et le nombre d’exemplaires de chaque livre 11. Pour chaque livre, donner le titre du livre et le nombre d’exemplaires dans l’état 10 8 12. Trouver la durée moyenne d’un emprunt 13. Afficher le nombre de locations par mois 14. Trouver l’âge du plus jeune auteur 15. Combien de fois chaque exemplaire d’un livre a-t-il été emprunté (titre, num exemplaire, nbre)? Présenter les résultats par ordre d’emprunt décroissant. 16. Idem mais uniquement pour les exemplaires empruntés plus d’une fois. 17. Afficher pour chaque catégorie si elle a une catégorie parent et si oui le nom de celleci 18. Idem en ajoutant un niveau supplémentaire Exercices supplémentaires 1. Afficher pour chaque livre, le titre du livre, les numéros d’exemplaires et l’état de chaque exemplaire. Ordonner le résultat par ordre alphabétique des titres et par ordre inverse de l’état de l’exemplaire. 2. Afficher pour chaque location, le nom et le prénom de l’emprunteur, le titre du livre emprunté et la date d’emprunt. Trier le résultat par date d’emprunt décroissante. 3. Afficher le titre, le nom des auteurs, le nom de l’éditeur et le nom de la catégorie de chaque livre. 4. Afficher le nombre de livres publiés entre 1970 et 2000 5. Afficher le nombre de livres par numéro de catégorie 6. Compter le nombre d’emprunteurs qui n’ont pas rendu un exemplaire 7. Trouver la durée moyenne d’un emprunt par emprunteur (afficher le nom de l’emprunteur) 8. Trouver la durée moyenne d’un emprunt pour les emprunteurs qui ne sont ni de Liège et ni de Bruxelles lorsque cette moyenne est comprise entre 25 et 35 (afficher la localité) 9. Quel est le plus grand retard parmi les livres rendus en retard ? 10. Trouver les cotisations totales payées chaque mois (par les emprunteurs). Afficher le total par mois dans l’ordre habituel des mois (janvier, février, …) 11. Idem pour les mois où la somme des cotisations est supérieure à 100 12. Afficher (une seule fois) le nom et le prénom des emprunteurs ayant loués des livres loués par des emprunteurs de la catégorie ‘Retraite’. 9 13. Afficher le nom de chaque catégorie qui a une catégorie parent et le nom de cette catégorie parent Bibliographie [1] [2] [3] [4] [5] C. MAREE et G. LEDANT, SQL-2 : Initiation, Programmation, 2ème édition, Armand Colin, 1994, Paris P. DELMAL, SQL2 – SQL3 : application à Oracle, 3ème édition, De Boeck Université, 2001, Bruxelles Microsoft, MSDN Microsoft Developper Network, http://msdn.microsoft.com, consulté en février 2009, Microsoft Corp. Diana Lorentz, et al., Oracle Database SQL Reference, 10g Release 2 (10.2), published by Oracle and available at http://www.oracle.com/pls/db102/homepage, 2005 JL. HAINAUT, Bases de données: concepts, utilisation et développement, Dunod, 2009, Paris. Remerciements Un merci particulier à mes collègues Vincent REIP et Vincent WILMET pour leur relecture attentive et leurs propositions de correction et d’amélioration. 10 Auteur NumAuteur Nom DateNaissance[0-1] id: NumAuteur acc Type NumType Description id: NumType acc Redaction NumAuteur NumLivre id: NumAuteur NumLivre acc equ: NumLivre acc ref: NumAuteur Langue CodeIso NomLangue id: CodeIso acc Categorie NumCategorie Nom CategorieParent[0-1] id: NumCategorie acc ref: CategorieParent acc Livre NumLivre Titre ISBN NbPages Cote Langue NumType NumCategorie NumEditeur id: NumLivre acc id': ISBN acc ref: Langue acc ref: NumType acc ref: NumCategorie acc ref: NumEditeur acc Editeur NumEditeur Nom Adresse Localite id: NumEditeur acc Exemplaire NumLivre NumExemplaire Etat DateAchat NumEtagere id: NumLivre NumExemplaire acc ref: NumEtagere ref: NumLivre Critique NumEmprunteur NumLivre Cote Commentaire id: NumEmprunteur NumLivre acc ref: NumLivre acc ref: NumEmprunteur Location NumEmprunteur NumLivre NumExemplaire DateLocation DateRetourPrevu DateRetour[0-1] id: NumEmprunteur NumLivre NumExemplaire DateLocation acc ref: NumLivre NumExemplaire acc ref: NumEmprunteur Emplacement NumEtagere Allee Etage id: NumEtagere Emprunteur NumEmprunteur Nom Prenom Adresse Localite Cotisation RegNat DatePaiementCotisation CatEmprunteur id: NumEmprunteur acc id': RegNat acc ref: CatEmprunteur acc CatEmprunteur NumCategorie Designation Cotisation id: NumCategorie acc 11