Questions/réponses proposées par Didier Boulle sur son site.
Résumé des tables présentes dans la base :
ARTICLES (NOART, LIBELLE, STOCK, PRIXINVENT) FOURNISSEURS (NOFOUR, NOMFOUR, ADRFOUR, VILLEFOUR) ACHETER (NOFOUR#, NOART#, PRIXACHAT, DELAI)
Connexion : psql -d commerce
SELECT NOART, LIBELLE
FROM ARTICLES
WHERE STOCK<10;
Algèbre relationnelle (bonus) :SELECT *
FROM ARTICLES
WHERE PRIXINVENT BETWEEN 100 AND 300;
AR :SELECT *
FROM FOURNISSEURS
WHERE ADRFOUR IS NULL;
AR :SELECT *
FROM FOURNISSEURS
WHERE NOMFOUR LIKE 'STE%';
AR :SELECT NOMFOUR, ADRFOUR, VILLEFOUR
FROM FOURNISSEURS F
JOIN ACHETER A
ON F.NOFOUR=A.NOFOUR
WHERE DELAI>20;
AR :SELECT COUNT(*) AS NbArticles
FROM ARTICLES;
AR :SELECT SUM(STOCK*PRIXINVENT) AS ValeurStock
FROM ARTICLES;
AR :SELECT NOART, LIBELLE, STOCK
FROM ARTICLES
ORDER BY STOCK DESC;
AR :SELECT A.NOART, LIBELLE, MAX(PRIXACHAT) AS PMAX,
MIN(PRIXACHAT) AS PMIN, AVG(PRIXACHAT) AS PMOY
FROM ACHETER A
JOIN ARTICLES B
ON A.NOART = B.NOART
GROUP BY A.NOART, LIBELLE;
AR :SELECT A.NOFOUR, NOMFOUR, AVG(DELAI) AS DelaiMoyen
FROM ACHETER A
JOIN FOURNISSEURS F
ON A.NOFOUR = F.NOFOUR
GROUP BY A.NOFOUR, NOMFOUR
HAVING COUNT(*) >=2;
AR :SELECT a.noart, libelle, nomfour, minprix
FROM acheter a
JOIN
(select noart, min(prixachat) as minprix
FROM acheter
group by noart) PMIN
ON a.noart = PMIN.noart AND a.prixachat = PMIN.minprix
JOIN fournisseurs f
ON a.nofour = f.nofour
JOIN articles l
ON a.noart = l.noart;
AR :SELECT f.nofour, nomfour, adrfour, villefour
FROM fournisseurs f
LEFT JOIN acheter a
on f.nofour = a.nofour
WHERE a.noart IS NULL;
AR :SELECT nomfour, a.prixachat as prix_100,
b.prixachat as prix_106
FROM acheter a
join acheter b
using(nofour)
JOIN fournisseurs
using(nofour)
WHERE a.noart=100 AND b.noart=106;
AR :SELECT a.noart, libelle, nomfour, a.prixachat, a.delai
FROM acheter a
JOIN acheter b
ON a.noart = b.noart
JOIN fournisseurs f
ON a.nofour = f.nofour
JOIN articles l
ON a.noart = l.noart
WHERE a.nofour <> b.nofour
order by a.noart;
AR :SELECT nofour, nomfour, count(*) as nombre_produits
FROM fournisseurs
join acheter
using(nofour)
GROUP BY nofour, nomfour
HAVING count(*) = (SELECT MAX(nb_prod) FROM
(SELECT count(*) as nb_prod
FROM acheter
GROUP BY nofour) A);
AR :Exercices proposés par Christine Keribin dans un TP antérieur.
Vous travaillez dans une agence immobilière qui a mis en place un modèle relationnel afin de gérer son portefeuille client. Le modèle relationnel est le suivant :
CLIENT (codeclt, nomclt, prenomclt, adresseclt, CPclt, villeclt) APPARTEMENT (ref, superficie, pxvente, secteur, #coderep, #codeclt) REPRESENTANT (coderep, nomrep, prenomrep)
L'agent immobilier souhaite avoir un certain nombre d'informations. Écrivez les requêtes SQL correspondant aux demandes suivantes.
SELECT *
FROM CLIENT
ORDER BY nomclt;
SELECT * FROM APPARTEMENT APT
JOIN CLIENT CL
ON CL.codeclt = APT.codeclt
JOIN REPRESENTANT REP
ON REP.coderep = APT.coderep
WHERE CL.villeclt = 'Villefranche'
AND REP.prenomrep = 'Carole'
AND REP.nomrep = 'Palege';
SELECT secteur, AVG(pxvente) AS prixMoyen
FROM APPARTEMENT
GROUP BY secteur;
SELECT COUNT(*)
FROM APPARTEMENT
WHERE superficie > 80;
DELETE FROM APPARTEMENT
WHERE ref = '2006A';
b]
UPDATE APPARTEMENT
SET pxvente = 230000
WHERE ref = '2014G';
Le responsable du SAV d'une entreprise d'électroménager a mis en place une petite base de données afin de gérer les interventions de ces techniciens. Le modèle relationnel à la source de cette base de données est le suivant :
CLIENT (codeclt, nomclt, prenomclt, adresse, cp, ville) PRODUIT (référence, désignation, prix) TECHNICIEN (codetec, nomtec, prenomtec, tauxhoraire) INTERVENTION (numero, date, raison, #codeclt, #référence, #codetec)
Le responsable vous demande d’écrire en langage SQL les requêtes suivantes :
SELECT reference,designation
FROM PRODUIT
ORDER BY prix;
SELECT T.codetec, T.nomtec, T.prenomtec, I.nbInterv
FROM TECHNICIEN T
JOIN
(SELECT codetec, COUNT(*) AS nbInterv
FROM INTERVENTION
GROUP BY codetec) AS I
ON T.codetec = I.codetec;
SELECT designation, AVG(prix) AS prixMoyen
FROM PRODUIT
GROUP BY designation
HAVING AVG(prix) > 300;
Note: deux fois le calcul AVG(prix) : on devrait pouvoir l'éviter...
SELECT *
FROM CLIENT
WHERE codeclt IN
(SELECT codeclt
FROM INTERVENTION
WHERE reference IN
(SELECT reference
FROM PRODUIT
WHERE prix > 300));
SELECT *
FROM INTERVENTION
WHERE codetec = 2381 AND
date BETWEEN '2006-07-01' AND '2006-08-31';
UPDATE PRODUIT
SET prix = 320
WHERE reference = '548G';
b]
INSERT INTO TECHNICIEN
VALUES(3294, 'Cavacho', 'Denis', 15);
Questions trouvée dans un TP de Christine Keribin, utilisant une base construite par Philippe Rigaux
CREATE TABLE Internaute (
email VARCHAR (40) NOT NULL,
nom VARCHAR (30) NOT NULL ,
prenom VARCHAR (30) NOT NULL,
region VARCHAR (30),
PRIMARY KEY (email));
CREATE TABLE Pays (
code VARCHAR(4) NOT NULL,
nom VARCHAR (30) DEFAULT ’Inconnu’ NOT NULL,
langue VARCHAR (30) NOT NULL,
PRIMARY KEY (code));
CREATE TABLE Artiste (
idArtiste INTEGER NOT NULL,
nom VARCHAR (30) NOT NULL,
prenom VARCHAR (30) NOT NULL,
anneeNaiss INTEGER,
PRIMARY KEY (idArtiste),
UNIQUE (nom, prenom));
CREATE TABLE Film (
idFilm INTEGER NOT NULL,
titre VARCHAR (50) NOT NULL,
annee INTEGER NOT NULL,
idMES INTEGER,
genre VARCHAR (22) NOT NULL,
resume TEXT,
codePays VARCHAR (4),
PRIMARY KEY (idFilm),
FOREIGN KEY (idMES) REFERENCES Artiste,
FOREIGN KEY (codePays) REFERENCES Pays);
CREATE TABLE Notation (
idFilm INTEGER NOT NULL,
email VARCHAR (40) NOT NULL,
note INTEGER NOT NULL,
PRIMARY KEY (idFilm, email));
CREATE TABLE Role (
idFilm INTEGER NOT NULL,
idActeur INTEGER NOT NULL,
nomRole VARCHAR(32),
PRIMARY KEY (idActeur,idFilm),
FOREIGN KEY (idFilm) REFERENCES Film,
FOREIGN KEY (idActeur) REFERENCES Artiste);
CREATE TABLE Genre (
code VARCHAR (22) NOT NULL,
PRIMARY KEY (code));
Connexion : psql -d cinema
Remarque : la table "Artiste" contient les acteurs, mais aussi entre autres des metteurs en scène, qui n’ont pas forcément joué dans un film. C’est pourquoi on lit trois noms différents (idMES, idArtiste, idActeur) au fil des tables, mais ils référencent tous la table "Artiste".
SELECT nom, prenom
FROM Artiste
WHERE idArtiste IN
(SELECT idActeur
FROM Role
WHERE nomRole = 'Morpheus');
SELECT nom, prenom
FROM Artiste
WHERE idArtiste IN
(SELECT idMES
FROM Film
WHERE titre = 'Alien');
WITH NoteEqual4 AS
(SELECT email, idFilm
FROM Notation
WHERE note = 4)
SELECT F.titre, I.prenom, I.nom
FROM NoteEqual4 N
JOIN Internaute I
ON N.email = I.email
JOIN Film F
ON N.idFilm = F.idFilm
WITH ActeurRoleVertigo AS
(SELECT idActeur, nomRole
FROM Role
WHERE idFilm IN
(SELECT idFilm
FROM Film
WHERE titre = 'Vertigo'))
SELECT A.nom, A.prenom, ARV.nomRole
FROM Artiste A
JOIN ActeurRoleVertigo ARV
ON A.idArtiste = ARV.idActeur
SELECT *
FROM Film
WHERE idRES IN
(SELECT idArtiste
FROM Artiste
WHERE prenom = 'Tim' AND nom = 'Burton')
AND idFilm IN
(SELECT idFilm
FROM Role
WHERE idActeur IN
(SELECT idArtiste
FROM Artiste
WHERE prenom = 'Johnny' AND nom = 'Depp'));
SELECT F.titre, R.nomRole
FROM Role R
JOIN Artiste A
ON A.idArtiste = R.idActeur
JOIN Film F
ON F.idFilm = R.idFilm
WHERE A.prenom = 'Bruce' AND A.nom = 'Willis';
-- Note : serait plus efficace avec des requêtes en plusieurs temps, comme ci-dessus.
SELECT DISTINCT A.prenom, A.nom
FROM Artiste A
WHERE EXISTS
(SELECT 1
FROM Role R
WHERE R.idActeur = A.idArtiste AND R.idActeur IN
(SELECT idMES FROM Film WHERE idFilm = R.idFilm));
WITH MemePrenom AS
(SELECT A1.idArtiste AS idArtiste1, A2.idArtiste AS idArtiste2,
A1.prenom, A1.nom AS nom1, A2.nom AS nom2
FROM Artiste A1
JOIN Artiste A2
ON A1.prenom = A2.prenom AND A1.idArtiste != A2.idArtiste)
SELECT F.titre, MP.prenom, MP.nom1 AS nomMES, MP.nom2 AS nomActeur
FROM Film F
JOIN MemePrenom MP
ON MP.idArtiste1 = F.idMES
WHERE MP.idArtiste2 IN
(SELECT R.idActeur
FROM Role R
WHERE R.idFilm = F.idFilm);
-- TODO: sans doute pas optimal...
WITH NotesRigaux AS
(SELECT note
FROM Notation
WHERE email = 'rigaux@cnam.fr')
SELECT COUNT(*) AS nombreDeFilms, AVG(note) AS noteMoyenne,
MIN(note) AS noteMin, MAX(note) AS noteMax
FROM NotesRigaux;
SELECT COUNT(*)
FROM Role
WHERE nomRole = 'McCane' AND idActeur IN
(SELECT idArtiste
FROM Artiste
WHERE prenom = 'Bruce' AND nom = 'Willis');
SELECT A.idArtiste, A.nom, A.prenom, IdNb.nombreDeFilms
FROM Artiste A
JOIN
(SELECT idMES, COUNT(*) AS nombreDeFilms
FROM Film
GROUP BY idMES) AS IdNb
ON A.idArtiste = IdNb.idMES;
SELECT *
FROM Film F
WHERE NOT EXISTS
(SELECT 1
FROM Role R
WHERE F.idFilm = R.idFilm);
SELECT nom, prenom
FROM Artiste
WHERE idArtiste NOT IN
(SELECT idMES
FROM Film);
SELECT *
FROM Internaute
WHERE email NOT IN
(SELECT email
FROM Notation
WHERE idFilm IN
(SELECT idFilm
FROM Film
WHERE annee = 1999));
SELECT nom, prenom
FROM Artiste
WHERE idArtiste IN
(SELECT idMES
FROM Film);
SELECT titre, annee
FROM Film
WHERE genre IN
(SELECT genre
FROM Film
WHERE titre = 'Matrix');
SELECT I.nom, N.note
FROM Notation N
JOIN Internaute I
ON N.email = I.email
WHERE N.idFilm IN
(SELECT idFilm
FROM Film
WHERE titre = 'Alien');
SELECT A.nom, R.nomRole, F1.titre
FROM Role R
JOIN Artiste A
ON R.idActeur = A.idArtiste
JOIN Film F1
ON F1.idFilm = R.idFilm
WHERE A.idArtiste <> F1.idMES AND A.idArtiste IN
(SELECT F2.idMES
FROM Film F2);
Se connecter à la base après connexion SSH : psql -d dvdrental
Pour connaître le schéma d'une table : \d <nom_table>
Voir aussi le tutoriel correspondant, et le schéma des tables
SELECT title
FROM film_actor
JOIN film
ON film.film_id = film_actor.film_id
JOIN actor
ON actor.actor_id = film_actor.actor_id
WHERE actor.first_name='Henry' AND actor.last_name='Berry';
SELECT sto.store_id, COUNT(*) AS filmsDispo
FROM store sto
JOIN inventory i
ON sto.store_id=i.store_id
JOIN staff sta
ON sto.store_id=sta.store_id
JOIN rental r
ON r.staff_id=sta.staff_id
WHERE r.return_date IS NULL OR r.return_date < NOW()
GROUP BY sto.store_id
HAVING COUNT(*) > 100;
-- Note : sur la base dvdrental tous les DVD sont retournés car toutes les dates sont avant NOW()
-- sur une vraie base actualisée en temps réel, ce serait plus intéressant.
SELECT first_name, last_name, address, address2, district, city, country
FROM customer
-- ...
Je ne l'écris pas car c'est linéaire, long et peu intéressant : implique les tables address, city, country, rental, film (JOIN, JOIN, ...), puis WHERE rental.return_date > NOW() AND film.title = 'blabla'.
En fait, face à une telle requête à rallonge on peut se demander s'il est très efficace d'autant normaliser la base : cf. NoSQL, voir séance 3 "conception des BdD".
SELECT COUNT(*)
FROM film_category fg
WHERE fg.category_id IN
(SELECT ca.category_id
FROM category ca
WHERE ca.name = 'Horror');
SELECT SUM(p.amount)
FROM payment p
WHERE p.customer_id IN
(SELECT cu.customer_id
FROM customer cu
WHERE cu.first_name='Donna' AND cu.last_name='Thompson');
SELECT ac.first_name, ac.last_name
FROM actor ac
WHERE ac.actor_id NOT IN
(SELECT fa.actor_id
FROM film_actor fa
WHERE fa.film_id IN
(SELECT fi.film_id
FROM film fi
JOIN language l
ON fi.language_id = l.language_id
WHERE l.name = 'German'));
-- On peut aussi le faire avec IN au lieu de NOT IN, et <> 'German' dans la dernière clause WHERE...
-- Note : sur la base dvdrental tous les films sont libellés 'English'...
-- donc la requête renvoie tous les acteurs
SELECT fi.film_id, fi.title
FROM film fi
WHERE fi.film_id NOT IN
(SELECT i.film_id
FROM inventory i
JOIN rental r
ON i.inventory_id = r.inventory_id);
-- Que se passerait-il avec un OUTER JOIN ?
-- (On renverrait probablement tous les films)