name: inverse layout: true class: center, middle, inverse --- # Base de données - CM5 ## Inès de Courchelle ## 2024-2025  --- layout: false # Rappel ## NE PAS OUBLIER
--- # Rappel ## Quelle est la différence entre INSERT et CREATE ? - .under[INSERT] permet d'inserer des données dans une table -
CREATE
permet de créer une table ## Quelle est la différence entre UPDATE et ALTER ? -
UPDATE
permet de mettre à jour une donnée -
ALTER
permet de mettre à jour la structure de la table ## Quelle est la différence entre DELETE et DROP ? - .under[DELETE] permet de supprimer une donnée - .underG[DROP] permet de supprimer la table --- # PLAN ## Les requêtes approfondies : 1. Les sous requêtes 2. Union 3. intersection 4. Différences 5. Les jointures --- # Les sous requêtes ## Pourquoi ? Permettre de faire une requête sur la base d'un autre résultat. ## Comment ? - Utiliser des requêtes imbriquées - Stocker des résultats de requêtes
--- # Les sous requêtes ## Plusieurs cas possibles : - dans les conditions ! - Sous requêtes à valeur unique - Sous requêtes à colonne multiples - Sous requêtes en renvoyant une liste - dans les Sélections !
--- # Sous Requête à valeur unique ## Pourquoi ? Comparer deux valeurs ## Opérateurs disponibles >, <, <=,>=,< >, = ## Exemple | idJoueur | nom | prenom | poste | nb buts | | ----------- | ----- | ----- | ---- | ---- | | 01 | Ronaldo | Cristiano | Attaquant | 42 | | 02 | Mbappé | Kylian | Attaquant | 37 | | 03 | Ozil | Mesut | Milieu | 1 | | 04 | Zidane | Zinedine | Milieu | 12 | | 05 | Giroud | Olivier | Attaquant | 42 | | 06 | Varane | Raphael | Defenseur | 10 | .underR[Comment avoir tous les joueurs jouants au même poste que Kylian ?] --- # Sous Requête à valeur unique | idJoueur | nom | prenom | poste | nb buts | | ----------- | ----- | ----- | ---- | ---- | | 01 | Ronaldo | Cristiano | Attaquant | 42 | | 02 | Mbappé | Kylian | Attaquant | 37 | | 03 | Ozil | Mesut | Milieu | 1 | | 04 | Zidane | Zinedine | Milieu | 12 | | 05 | Giroud | Olivier | Attaquant | 42 | | 06 | Varane | Raphael | Defenseur | 10 | ```sql SELECT nom,prenom FROM Joueur WHERE poste="Attaquant"; ``` - Ici, on connaît le poste de Kylian ! - Comment faire si on n'a pas son poste ? --- # Sous Requête à valeur unique ## Solution ```sql SELECT nom,prenom FROM Joueur Where poste = (SELECT poste FROM Joueur WHERE prenom="Kylian" ); ``` ## En français **On peut traduire la requête par :** *Séletionner le nom et le prénom de tous les joueurs qui ont le même poste que le poste auquel Kylian évolue*
--- # Sous Requête à valeur unique ## Autre exemple Séletionner le nom et le prénom de tous les joueurs qui ont marqué plus de but que Raphael Varane : ```sql SELECT nom, prenom FROM Joueur WHERE nbButs > (SELECT nbButs FROM Joueur WHERE nom = "Varane" AND prenom = "Raphael"); ```
--- # Sous Requête à colonnes multiples ## Exemple Sélectionner le nom et le prénom de tous les joueurs qui ont marqué autant de but et qui jouent au même poste que Olivier Giroud. ```sql SELECT nom, prenom FROM Joueur WHERE (poste,nbButs)= (SELECT poste, nbButs FROM Joueur WHERE nom="Giroud" AND prenom="Olivier"); ``` **Attention les deux colonnes présentes dans le where doivent être les mêmes que dans le select de la sous requête !** --- # Sous Requête renvoyant une liste ## IN - la valeur doit être dans le résultat de la requête - SELECT ... FROM ... WHERE nom IN (SELECT nom FROM BallonDor) ## NOT IN - la valeur ne doit pas être dans le résultat de la requête - SELECT ... FROM ... WHERE nom NOT IN (SELECT nom) ## ALL - la valeur tester doit être <,>, =, >= ,<=, < > à tous les résultats de la requête - SELECT ... FROM ... WHERE nbButs >= ALL (SELECT nbButs FROM OM) ## ANY - la valeur tester doit être au moins <,>, =, >= ,<=, < > à un des résultats - SELECT ... FROM ... WHERE nbButs >= ANY (SELECT nbButs FROM PSG) --- # Sous Requête renvoyant une liste ## Similitudes - = ANY est équivalent à IN - != All est équivalent à NOT IN
--- # Illustration ## La Table Joueur | idJoueur | nom | prenom | poste | nb buts | | ----------- | ----- | ----- | ---- | ---- | | 01 | Ronaldo | Cristiano | Attaquant | 42 | | 02 | Mbappé | Kylian | Attaquant | 37 | | 03 | Ozil | Mesut | Milieu | 1 | | 04 | Zidane | Zinedine | Milieu | 12 | | 05 | Giroud | Olivier | Attaquant | 42 | | 06 | Varane | Raphael | Defenseur | 10 | ## La Table Remplacement | idJoueur1 | idJoueur2 | idMatch | date | | -------- | --------- | ------ | ------ | | 01 | 02 | 02 | 2020-03-05 21:05:45 | | 02 | 03 | 01 | 2020-04-15 22:17:31 | | 05 | 06 | 02 | 2020-04-15 14:25:11 | | 01 | 06 | 02 | 2020-10-15 15:02:23 | --- # Exemple IN Selectionner le nom et le prénom des joueurs qui ont été remplacé au moins une fois ```sql SELECT nom, prenom FROM Joueur WHERE idJoueur IN (SELECT idJoueur1 FROM Remplacement ); ``` | nom | prenom | |---------|-----------| | Ronaldo | Cristiano | | Mbappé | Kylian | | Giroud | Olivier |
--- # Exemple NOT IN Selectionner le nom et le prénom des joueurs qui n'ont jamais été remplacé ```sql SELECT nom, prenom FROM Joueur WHERE idJoueur NOT IN (SELECT idJoueur1 FROM Remplacement ); ``` | nom | prenom | |--------|----------| | Ozil | Mesut | | Zidane | Zinedine | | Varane | Raphael |
--- # Exemple ALL 1/2 ## Ce qu'on veut Selectionner le nom et le prénom des joueurs qui ont marqué le plus de buts #### 1- Selectionner le nombre de but de tous les joueurs ```sql SELECT nbButs FROM Joueur; ``` #### 2- Selectionner le nom et le prénom des joueurs qui ont marqué plus de n buts ! ```sql SELECT nom,prenom FROM Joueur WHERE nbButs>=N; ``` --- # Exemple ALL 2/2
#### 3 - On fusionne les deux selects ! ```sql SELECT nom,prenom FROM Joueur WHERE nbButs >= ALL (SELECT nbButs FROM Joueur ); ``` --- # Exemple ANY 1/4 **Selectionner les joueurs qui n'ont jamais été remplacés** #### 1- Selectionner les joueurs dans la table Remplacement ```sql SELECT idJoueur1 FROM Remplacement ``` #### 2- Selectionner l'ensemble des joueurs ```sql SELECT nom,prenom FROM Joueur ``` --- # Exemple ANY ## FUSION
```sql SELECT nom,prenom FROM Joueur WHERE idJoueur <> ANY (SELECT idJoueur1 FROM Remplacement ); ``` --- # Exemple ANY ## ANY est obligatoire #### MAIS POURQUOI ? CAR - La sous requête renvoie un tableau de résultat - Un idJoueur peut apparaître plusieurs fois dans la table remplacement (pour 2 matchs différents) ## La Table Remplacement | idJoueur1 | idJoueur2 | idMatch | date | | -------- | --------- | ------ | ------ | |
01
| 02 | 02 | 2020-03-05 21:05:45 | | 02 | 03 | 01 | 2020-04-15 22:17:31 | | 05 | 06 | 02 | 2020-04-15 14:25:11 | |
01
| 06 | 02 | 2020-10-15 15:02:23 | --- # Exemple ANY 4/4
*ici le joueur 1 apparaît deux fois* | idJoueur1 | idJoueur2 | idMatch | date | | -------------- | --------- | ------ | ------ | |
01
| 02 | 02 | 2020-03-05 21:05:45 | | 02 | 03 | 01 | 2020-04-15 22:17:31 | | 05 | 06 | 02 | 2020-04-15 14:25:11 | |
01
| 06 | 02 | 2020-10-15 15:02:23 | --- # ATTENTION *Faire bien la différence entre les sous requêtes en mode liste et en mode une ou deux valeurs :* .pull-left[ ```sql SELECT nom, prenom FROM Joueur WHERE idJoueur = (SELECT idJoueur1 FROM Remplacement ); ```
] .pull-right[
]
- le = doit comparer une valeur A avec une valeur B - ICI - c'est comme ci on comparait A avec un tableau -
A = [A,B,C]
*Ceci n'est pas possible* --- # ATTENTION *ICI, la solution est d'utiliser
IN
* ```sql SELECT nom, prenom FROM Joueur WHERE idJoueur IN (SELECT idJoueur1 FROM Remplacement ); ```
--- # Les requêtes imbriquées ## Squelette général ```sql SELECT < nom_colonne > FROM < nom_table > WHERE < nom_colonne > [>= | < | = | ...] [ANY | ALL | IN ...] ( SELECT
(< nom_colonne >) FROM < nom_table > ); ``` **Opérateurs :** COUNT, SUM, AVG ... --- # Exemple **Selectionner les joueurs qui ont un nombre de but supérieur à la moyenne** ```sql SELECT nom,prenom FROM Joueur WHERE nbButs >= ( SELECT AVG(nbButs) as moyenne FROM Joueur ); ``` **Comment faire pour afficher en plus la moyenne générale ?**
--- # Les requêtes imbriquées SELECT ## Step by step #### 1. La requête pour trouver la moyenne de buts générale ! ```sql SELECT AVG(nbButs) FROM Joueur; ``` #### 2. Fusion !!! - C'est dans l'affichage que l'on souhaite avoir la moyenne - Donc la requête imbriqué est dans le SELECT .pull-left-small[
] .pull-right-big[ ```sql SELECT nom,prenom, (SELECT AVG(nbButs) FROM Joueur) FROM Joueur WHERE nbButs >= ( SELECT AVG(nbButs) as moyenne FROM Joueur ); ``` ] --- # Les sous requêtes dans les FROMs ## Squelette générale
.underR[Attention] - Il faut obligatoirement utiliser le mot clé
as
- Il faut obligatoirement donner un nom à la table dérivée --- # GROUP BY ## Quand ? Lorsque l'on utilise les fonctions d'agregats (SUM, AVG, ...) ## Pourquoi ? Pour regrouper des données ## Comment ? À la toute fin de la requête ## TIP Le
group by
doit toujours contenir les colonnes appelées dans le select (sauf celles utilisées dans la fonction d'agregat) --- # GROUP BY Nous considérons les trois tables suivantes .pull-left[ | idConcert | titre | | ----- | ---- | | 01 | Woodstock | | 02 | Monterey Pop Festival | | 03 | Hellfest | | 04 | Garorock |
| idMusicien | nom | |------------|---------------| | 01 | Lemmy | | 02 | Country Joe | | 03 | Steel Panther | | 04 | Canned Heat | ] .pull-right[ | #idConcert | #idMusicien | dateConcert | | --- | ---- | --- | | 01 | 02 | 1969 | | 02 | 04 | 1967 | | 03 | 03 | 2017 | | 03 | 01 | 2015 | | 04 | 05 | 2013 | | 03 | 01 | 2006 | | 03 | 03 | 2019 | | 03 | 01 | 2010 | ] --- # GROUP BY ## Ce que je veux Calculer le nombre de concert par musicien ## Reformulation Je dois compter le nombre de fois où apparaît le même idMusicien dans la table Jouer ```sql SELECT idMusicien, count(idMusicien) FROM Jouer ``` .pull-left[ | #idConcert | #idMusicien | dateConcert | | --- | ---- | --- | | 04 | 05 | 2013 | | 03 | 01 | 2006 | | 03 | 03 | 2019 | | 03 | 01 | 2010 | ] .pull-right[ | #idConcert | #idMusicien | dateConcert | | --- | ---- | --- | | 01 | 02 | 1969 | | 02 | 04 | 1967 | | 03 | 03 | 2017 | | 03 | 01 | 2015 | ] Comment dois je compter les *idMusicien* ? Par total ? Par nombre de fois où un *idMusicien* apparaît ? --- # GROUP BY ## ERROR ```sql SELECT idMusicien, count(idMusicien) FROM Jouer ```
#### Mais comment faire ? --- # GROUP BY ## Reformulation 2.0 Je dois compter le nombre de fois où apparaît le même idMusicien dans la table Jouer ```sql SELECT idMusicien, count (idMusicien) FROM Jouer GROUP BY idMusicien ``` .pull-left[ | #idConcert | #idMusicien | dateConcert | | --- | ---- | --- | | 01 | 02 | 1969 | | 02 | 04 | 1967 | | 03 | 03 | 2017 | | 03 | 01 | 2015 | | 04 | 05 | 2013 | | 03 | 01 | 2006 | | 03 | 03 | 2019 | | 03 | 01 | 2010 | ] .pull-right[
] --- # HAVING ## À quoi ça sert ? Il permet de réaliser des conditions sur une fonction d'agregat réalisé dans le select ## SQUELETTE ```sql SELECT
,
, [count|sum|avg|...](
) FROM Table GROUP BY
,
having [count|sum|avg|...](
) [=|<|>|...]
; ```
--- # HAVING ## Ce que je veux Calculer le nombre de concert par musicien et n'afficher que ceux qui ont jouer qu'une fois ! ## solution 1 .pull-left[ ```sql SELECT idMusicien, count(idMusicien) FROM Jouer GROUP BY idMusicien having count(idMusicien)=1; ``` ] .pull-right[
] ## solution 2 .pull-left[ ```sql SELECT idMusicien, count(idMusicien) as total FROM Jouer GROUP BY idMusicien having count(total)=1; ``` ] .pull-right[
] --- # Illustration ## MCD
## MLD Consommateur(
idConsommateur
,nom,prenom)
Magasin(
idMagasin
,nom,adresse)
Acheter(
#idConsommateur,#idMagasin,date
)
**Quels sont les consommateurs qui ont réalisés des achats à plus de 2 magasins ?** --- # Illustration ## Solution ```sql SELECT DISTINCT idConsommateur, SUM(1) FROM ( SELECT DISTINCT idConsommateur, idMagasin FROM Acheter ) AS conso GROUP BY idConsommateur HAVING 1 < SUM(1); ```
--- # limit ## Définition C'est un petit peu le
more
que l'on a vu en commandes unix ! ## Utilisation .pull-left[
] .pull-right[
] --- # UNION ## À quoi sert-il ? Il permet d'unir deux résultats de requêtes en un ## Comment ? avec le mot clé *UNION* .pull-left[
] .pull-right[
] .pull-left[ ```sql SELECT id FROM t1 UNION SELECT id FROM t2 ``` ] .pull-right[
] --- # INTERSECTION ## À quoi sert-il ? Il permet d'obtenir l'intersection de deux résultats de requêtes en un ## Comment ? avec le mot clé *INTERSECT* .pull-left[
] .pull-right[
] .pull-left[
```sql SELECT id FROM t1 INTERSECT SELECT id FROM t2 ``` **Attention en mySQL il n'est pas dispo !** ] .pull-left[
] --- # MINUS ## À quoi sert-il ? Il permet d'unir deux résultats de requête en un ## Comment ? avec le mot clé *MINUS* .pull-left[
] .pull-right[
] .pull-left[
```sql SELECT id FROM t1 MINUS SELECT id FROM t2 ``` **Attention en mySQL il n'est pas dispo !** ] .pull-right[
] --- # Help ## Comment faire pour réaliser des MINUS et des INTERSECT ?
## Avec des jointures ...
--- # Jointure avec pivot Nous considérons les trois tables suivantes .pull-left[ | idConcert | titre | | ----- | ---- | | 01 | Woodstock | | 02 | Monterey Pop Festival | | 03 | Hellfest | | 04 | Garorock | | idMusicien | nom | |------------|---------------| | 01 | Lemmy | | 02 | Country Joe | | 03 | Steel Panther | | 04 | Canned Heat | ] .pull-right[ | #idConcert | #idMusicien | dateConcert | | --- | ---- | --- | | 01 | 02 | 1969 | | 02 | 04 | 1967 | | 03 | 03 | 2017 | | 03 | 01 | 2015 | | 04 | 05 | 2013 | | 03 | 01 | 2006 | | 03 | 03 | 2019 | | 03 | 01 | 2010 | ] Nous désirons connaître les noms des musiciens ayant jouer dans un festival avant 2010 ! --- # Jointure avec pivot ## Problème .pull-left[ ```sql SELECT idMusicien FROM Jouer WHERE YEAR(dateConcert) < 2010 ``` ] .pull-right[ Ici problème j'ai l'id mais pas le nom dans le résultat ]
## Solution .pull-left[ #### La requête ```sql SELECT Musicien.nom FROM Jouer, Musicien WHERE YEAR(dateConcert) < 2010 AND Jouer.idMusicien=Musicien.idMusicien ``` *La dernière étape est la jointure avec pivot elle nous permet de faire le lien entre l'id du musicien contenu dans la table Jouer* ] .pull-right[ #### Explication 1. le nom du musicien en résultat 2. Utilisation de Jouer pour comparer les dates et Musicien pour obtenir le nom du musicien 3. Sélection de l'année de chaque date et comparaison à 2010 4. Si les musiciens sont les mêmes alors c'est bon ! ] --- # Jointure avec pivot ## Attention .pull-left[ **Si j'oublie le pivot mais que je mets le nom**
*cela me sort tous les noms de la table Musicien* ] .pull-right[ **si j'oublie de préciser d'où provient l'idMusicien**
*cela me donne une erreur car la colonne a le même nom dans les deux tables* ]
--- # Jointure avec pivot ## Si on veut l'id ! .pull-left[
] .pull-right[
]
Il faut donc préciser dans le select à qui appartient le idMusicien -
Musicien
.idMusicien -
Jouer
.idMusicien --- # Jointure avec pivot ## Et si on est maniac (Pas de doublons)
--- # Jointure Interne 1/2 ## Définition Comme la jointure avec pivot elle permet de lier 2 colonnes dans 2 tables différentes ! ## Squelette ```sql SELECT
,
, ... FROM
INNER JOIN
ON
=
WHERE CONDITIONS ``` ## Si on reprends le même exemple ```sql SELECT nom FROM Jouer INNER JOIN Musicien ON Jouer.idMusicien= Musicien.idMusicien WHERE year(dateConcert) < 2010; ``` --- # Jointure Interne ## Si on ne veut pas de doublons ! ```sql SELECT DISTINCT(nom) FROM Jouer INNER JOIN Musicien ON Jouer.idMusicien= Musicien.idMusicien WHERE year(dateConcert) < 2010; ``` ## PIVOT ou JOINTURE INTERNE ? C'est la même !
--- # Faire une intersection en mySQL ## La solution .pull-left[ - Si l'attribut id a le même nom dans les deux tables ```sql SELECT DISTINCT id FROM t1 INNER JOIN t2 USING(id); ``` - S'ils n'ont pas le même nom, il faut le préciser ```sql SELECT DISTINCT t1.id FROM t1 INNER JOIN t2 ON t1.id = t2.id; ```
] .pull-right[
] --- # Back to the future ## Ameliorer les exemples précédents #### Le MCD
#### Le MLD Consommateur(
idConsommateur
,nom,prenom)
Magasin(
idMagasin
,nom,adresse)
Acheter(
#idConsommateur,#idMagasin,date
)
**Quels sont les consommateurs qui ont réalisés des achats à plus de 2 magasins ?** --- # Back to the future ## Avant .under[ICI, nous avions le résultat qu'avec l'ID] ```sql SELECT DISTINCT idConsommateur, SUM(1) FROM ( SELECT DISTINCT idConsommateur, idMagasin FROM Acheter ) AS conso GROUP BY idConsommateur HAVING 1 < SUM(1); ```
--- # Back to the future ## Après **Avec la jointure !** on peut retrouver le nom du consommateur ```sql SELECT DISTINCT conso.idConsommateur, c.nom , SUM(1) FROM ( SELECT DISTINCT Acheter.idConsommateur, idMagasin FROM Acheter ) AS conso INNER JOIN Consommateur c ON c.idConsommateur = conso.idConsommateur GROUP BY conso.idConsommateur,c.nom HAVING 1 < SUM(1); ```
--- # Back to the future ## Avant ```sql SELECT idMusicien, count(idMusicien) as total FROM Jouer GROUP BY idMusicien having count(total)=1; ``` .under[Calculer le nombre de concert par musicien et n'afficher que ceux qui ont jouer qu'une fois !]
.under[Comment faire pour ajouter le nom du musicien concerné ?]
--- # Back to the future ## no problem
## Solution **Il suffit de faire une jointure !** ```sql SELECT Jouer.idMusicien, m.nom,count(Jouer.idMusicien) as total FROM Jouer INNER JOIN Musicien m on m.idMusicien=jouer.idMusicien GROUP BY Jouer.idMusicien,m.nom having count(total)=1 ```