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