Exercice 1

Requêtes SQL sur la base "commerce"

Questions/réponses proposées par Didier Boulle sur son site.

Écrivez les requêtes suivantes en SQL et avec les opérateurs de l'algèbre relationnelle.

  1. Numéros et libellés des articles dont le stock est inférieur à 10 ?
  2. Liste des articles dont le prix d'inventaire est compris entre 100 et 300 ?
  3. Liste des fournisseurs dont on ne connaît pas l'adresse ?
  4. Liste des fournisseurs dont le nom commence par "STE" ?
  5. Noms et adresses des fournisseurs qui proposent des articles pour lesquels le délai d'approvisionnement est supérieur à 20 jours ?
  6. Nombre d'articles référencés ?
  7. Valeur du stock ?
  8. Numéros et libellés des articles triés dans l'ordre décroissant des stocks ?
  9. Liste pour chaque article (numéro et libellé) du prix d'achat maximum, minimum et moyen ?
  10. Délai moyen pour chaque fournisseur proposant au moins 2 articles ?
  11. Prix minimum de chaque article avec le fournisseur correspondant
  12. Fournisseurs auprès desquels on ne s'approvisionne plus et qu'il faudrait donc peut-être supprimer de la table Fournisseurs
  13. Quels sont les fournisseurs qui fournissent les 2 articles 100 et 106, et à quel prix ?
  14. Listes des articles pouvant être achetés chez plusieurs fournisseurs, avec le prix d'achat et le délai correspondants
  15. Quels sont le ou les fournisseurs qui fournissent le plus de produits ?

Solution

  1. SELECT NOART, LIBELLE
    FROM ARTICLES
    WHERE STOCK<10;

    Algèbre relationnelle :
    R1=SELECTION(ARTICLES, STOCK<10)
    R2=PROJECTION(R1, NOART, LIBELLE)

  2. SELECT *
    FROM ARTICLES
    WHERE PRIXINVENT BETWEEN 100 AND 300;

    AR :
    R1=SELECTION(ARTICLES, PRIXINVENT>=100 et PRIXINVENT<=300)
    R2=PROJECTION(R1, NOART, LIBELLE, STOCK, PRIXINVENT)

  3. SELECT *
    FROM FOURNISSEURS
    WHERE ADRFOUR IS NULL;

    AR :
    R1=SELECTION(FOURNISSEURS, ADRFOUR est "non renseigné")
    R2=PROJECTION(R1, NOFOUR, NOMFOUR, ADRFOUR, VILLEFOUR)

  4. SELECT *
    FROM FOURNISSEURS
    WHERE NOMFOUR LIKE 'STE%';

    AR :
    R1=SELECTION(FOURNISSEURS, NOMFOUR comme "STE%")
    R2=PROJECTION(R1, NOFOUR, NOMFOUR, ADRFOUR, VILLEFOUR)

  5. SELECT NOMFOUR, ADRFOUR, VILLEFOUR
    FROM FOURNISSEURS F 
    JOIN ACHETER A 
    	ON F.NOFOUR=A.NOFOUR
    WHERE DELAI>20;

    AR :
    R1=SELECTION(ACHETER, DELAI>20)
    R2=JOINTURE(R1, FOURNISSEURS, NOFOUR)
    R2=PROJECTION(R1, NOMFOUR, ADRFOUR, VILLEFOUR)

  6. SELECT COUNT(*) AS NbArticles
    FROM ARTICLES;

    AR :
    NbArticles=CALCULER(ARTICLES, Comptage())

  7. SELECT SUM(STOCK*PRIXINVENT) AS ValeurStock
    FROM ARTICLES;

    AR :
    ValeurStock=CALCULER(ARTICLES, Somme(STOCK*PRIXINVENT))

  8. SELECT NOART, LIBELLE, STOCK
    FROM ARTICLES
    ORDER BY STOCK DESC;

    AR :
    R1=PROJECTION(ARTICLES, NOART, LIBELLE, STOCK)
    R2=TRI(R1, STOCK décroissant)

  9. 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 :
    R1=REGROUPER_ET_CALCULER(ACHETER, NOART, PAMAX : MAX(PRIXACHAT), PAMIN : MIN(PRIXACHAT), PAMOY : MOYENNE(PRIXACHAT))
    R2=JOINTURE(R1, ARTICLES, NOART)
    R3=PROJECTION(R2, NOART, LIBELLE, PAMAX, PAMIN, PAMOY)

  10. 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 :
    R1=REGROUPER_ET_CALCULER(ACHETER, NOFOUR, Nb : Comptage(), DelaiMoyen : MOYENNE(DELAI))
    R2=SELECTION(R1, Nb>=2)
    R3=JOINTURE(R2, FOURNISSEURS, NOFOUR)
    R4=PROJECTION(R3, NOFOUR, NOMFOUR, DelaiMoyen)

  11. 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 :
    R1=REGROUPER_ET_CALCULER(ACHETER, NOART, minprix : MIN(prixachat))
    R2=JOINTURE(R1, ACHETER, R1.NOART=ACHETER.NOART et R1.minprix=ACHETER.prixachat)
    R3=JOINTURE(R2, FOURNISSEURS, NOFOUR)
    R4=JOINTURE(R3, ARTICLES, NOART)
    R5=PROJECTION(R3, NOART, LIBELLE, NOMFOUR, minprix)

  12. SELECT f.nofour, nomfour, adrfour, villefour
    FROM fournisseurs f 
    LEFT JOIN acheter a 
      on f.nofour = a.nofour
    WHERE a.noart IS NULL;

    AR :
    R1=PROJECTION(FOURNISSEURS, NOFOUR)
    R2=PROJECTION(ACHETER, NOFOUR)
    R3=DIFFERENCE(R1, R2)
    R4=JOINTURE(R3, FOURNISSEURS, NOFOUR)
    R5=PROJECTION(R4, NOFOUR, NOMFOUR, ADRFOUR, VILLEFOUR)
    ou
    R1=JOINTURE_GAUCHE(FOURNISSEURS, ACHETER, NOFOUR)
    R2=SELECTION(R1, NOART est NULL)
    R3=PROJECTION(R2, NOFOUR, NOMFOUR, ADRFOUR, VILLEFOUR)

  13. 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 :
    R1=SELECTION(ACHETER, NOART=100)
    R2=SELECTION(ACHETER, NOART=106)
    R3=JOINTURE(R1, R2, NOFOUR)
    R4=JOINTURE(R3, FOURNISSEURS, NOFOUR)
    R5=PROJECTION(R4, NOMFOUR, R1.prixachat, R2.prixachat)

  14. 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 :
    R1=ACHETER
    R2=JOINTURE(R1, ACHETER, NOART)
    R3=SELECTION(R2, R1.NOFOUR<>ACHETER.NOFOUR)
    R4=JOINTURE(R3, FOURNISSEURS, R1.NOFOUR=FOURNISSEURS.NOFOUR)
    R5=JOINTURE(R4, ARTICLES, R1.NOART=ACHETER.NOART)
    R6=PROJECTION(R5, R1.NOART, LIBELLE, NOMFOUR, R1.PRIXACHAT, R1.DELAI )

  15. 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 :
    R1=REGROUPER_ET_CALCULER(ACHETER, NOFOUR, Nb_prod : Comptage())
    Max_prod=CALCULER(R1, MAX(Nb_prod))
    R2=REGROUPER_ET_CALCULER(ACHETER, NOFOUR, Nombre_produits : Comptage())
    R3=SELECTION(R2, Nombre_produits=Max_prod)
    R4=JOINTURE(R3, FOURNISSEURS, NOFOUR)
    R5=PROJECTION(R4, NOFOUR, NOMFOUR, Nombre_produits)

Exercice 2

Requêtes SQL sur la base "cinéma"

Questions trouvée dans un TP de Christine Keribin, utilisant une base construite par Philippe Rigaux

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".

  1. Requêtes sur plusieurs tables
    1. Qui a joué le rôle de Morpheus (nom et prénom) ?
    2. Qui est le réalisateur de Alien ?
    3. Prénom et nom des internautes qui ont donné une note 4 à un film (donner aussi le titre du film)
    4. Quels acteurs ont joué quel rôle dans le film Vertigo?
    5. Films dont le réalisateur est Tim Burton et un des acteurs Johnny Depp.
    6. Titre des films dans lesquels a joué Bruce Willis. Donner le nom, le rôle et le titre des films.
    7. Quel metteur en scène a joué dans ses propres films ? Donner le rôle et le titre des films.
    8. Dans quels films le metteur en scène a-t-il le même prénom que l'un de ses interprètes ? (titre, nom du metteur en scène, nom de l'interprète).
  2. Agrégats
    1. Nombre de films notés par l'internaute rigaux@cnam.fr, moyenne des notes données, note minimale et note maximale.
    2. Combien de fois Bruce Willis a-t-il joué le rôle de McCane?
    3. id, nom et prénom des réalisateurs, et nombre de films qu'ils ont tournés
  3. Négation
    1. Films sans rôle.
    2. Nom et prénom des acteurs qui n'ont jamais mis en scène de film.
    3. Internautes qui n'ont pas noté de film paru en 1999.
  4. Requêtes imbriquées ou jointure ?
    1. Nom et prénom des artistes qui ont mis en scène un film
    2. Titre et l'année des films qui ont le même genre que Matrix
    3. Nom des internautes qui ont noté le film Alien. Donner également la note.
    4. Quel metteur en scène a joué en tant qu'acteur (mais pas dans son propre film) ? Nom, le rôle et titre des films où le metteur en scène a joué.

Solution

    1. SELECT nom, prenom
      FROM Artiste
      WHERE idArtiste IN
        (SELECT idActeur
        FROM Role
        WHERE nomRole = 'Morpheus');
    2. SELECT nom, prenom
      FROM Artiste
      WHERE idArtiste IN
        (SELECT idMES
        FROM Film
        WHERE titre = 'Alien');
    3. 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
    4. 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
    5. 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'));
    6. 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 étagées, comme ci-dessus.
    7. 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));
    8. 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...
    1. 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;
    2. SELECT COUNT(*)
      FROM Role
      WHERE nomRole = 'McCane' AND idActeur IN
        (SELECT idArtiste
        FROM Artiste
        WHERE prenom = 'Bruce' AND nom = 'Willis');
    3. 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;
    1. SELECT *
      FROM Film F
      WHERE NOT EXISTS
        (SELECT 1
        FROM Role R
        WHERE F.idFilm = R.idFilm);
    2. SELECT nom, prenom
      FROM Artiste
      WHERE idArtiste NOT IN
        (SELECT idMES
        FROM Film);
    3. SELECT *
      FROM Internaute
      WHERE email NOT IN
        (SELECT email
        FROM Notation
        WHERE idFilm IN
          (SELECT idFilm
          FROM Film
          WHERE annee = 1999));
    1. SELECT nom, prenom
      FROM Artiste
      WHERE idArtiste IN
        (SELECT idMES
        FROM Film);
    2. SELECT titre, annee
      FROM Film
      WHERE genre IN
        (SELECT genre
        FROM Film
        WHERE titre = 'Matrix');
    3. 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');
    4. 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);

Exercice 3

Écrivez quelques requêtes dans le réseau social...

Solution

Je n'enverrai probablement pas le réseau entièrement complété (ça restera un exercice, vous pouvez le faire : rien de trop difficile là dedans). Cependant, quelques mots sur la propriété "CASCADE" non vue en cours, et importante.

Contexte : action sur une table dont la clé est référencée ailleurs

CASCADE: A "CASCADE" action propagates the delete or update operation on the parent key to each dependent child key. For an "ON DELETE CASCADE" action, this means that each row in the child table that was associated with the deleted parent row is also deleted. For an "ON UPDATE CASCADE" action, it means that the values stored in each dependent child key are modified to match the new parent key values.

Exemple : voir la documentation, et/ou réfléchir au cas d'un post contenant des commentaires (clé étrangère = identifiant du post). Dans la modélisation suggérée on ne peut pas ajouter de contrainte (car "reference" peut être de nature Wall, Post, Group ou Event...), et il faut donc propager les opérations manuellement. Une modélisation alternative consiste à utiliser une table par type d'objet, en ajoutant "ON DELETE CASCADE".