- L3 MIASHS/Ingémath/METIS
- Université Paris Cité
- Année 2024-2025
- Course Homepage
- Moodle
Toutes les questions portent sur le schéma pagila
rappelé ci-dessous.
Pour chaque question, proposer une requête écrite en algèbre relationnelle OU en SQL
.
pagila
, films- Donner pour chaque film au catalogue, le titre et les prénoms des acteurs qui jouent dans ce film.
SELECT fi.title, string_agg(DISTINCT a.first_name, ', ') AS casting
FROM
pagila.film fi JOIN
ON (fi.film_id=fa.film_id)
pagila.film_actor fa JOIN
ON (fa.actor_id=a.actor_id)
pagila.actor a GROUP BY fi.film_id;
Il n’est pas nécessaire d’effectuer un groupement et une aggrégation, mais c’est une occasion d’illustrer cette possibilité. string_agg()
est une fonction proposée par PostgreSQL
, analogue à paste()
ou stringr::str_c()
en R
, ou à une invocation de functools.reduce()
en Python
:
>>> foo = lambda x,y : x + ', ' + y if x else y
>>> reduce(foo, ['Brad', 'Angelina', 'Romy', 'Ava'], '')
'Brad, Angelina, Romy, Ava'
Des fonctions d’aggrégations comme SUM(), MAX(), COUNT()
sont de cette forme. Des fonctions d’aggrégations comme AVG()
, VAR()
, COUNT(DISTINCT ...)
sont un peu plus compliquées à réaliser mais relève du même genre de calcul.
- Lister les titres de films qui ne sont disponibles que dans une seule langue
- 1
-
Partitionnement sur
title
- 2
- Filtration des classes de la partition à partir d’une condition sur l’aggrégat
- 3
- Projection sur l’attribut de partitionnement (pas d’aggrégat utile ici)
Il n’est pas indispensable de passer par un groupement/aggrégation. On peut obtenir le résultat en utilisant simplement sélection, projection, jointure, opérations ensemblistes (\(∩, ∪, ∖\)).
(SELECT
DISTINCT title
FROM
pagila.film
)EXCEPT
(SELECT
DISTINCT f1.title
FROM
pagila.film f1 JOIN
pagila.film f2ON (f1.title = f2.title AND f1.language_id < f2.language_id)
)
on est parfois tenté de choisir comme condition de jointure dans la seconde sous-requête f1.film_id = f2.film_id AND f1.language_id < f2.language_id
. Cela conduira a un résultat sans intérêt. L’attribut film_id
est clé primaire (primary key
) de film
(cela fait partie du schéma, cette contrainte s’impose à toutes les instances du schéma). Quelle que soit l’instance, on ne pourra pas trouver dans la table film
, deux tuples qui coïncident sur film_id
et diffèrent sur language_id
.
- Pour chaque langue originale (
original_language_id
), chaque langue de diffusion (language_id
), compter le nombre de titres tournés dans la langue originale et traduits dans la langue de diffusion (sans doublons).
- Donner pour chaque catégorie (de film), les noms des acteurs qui apparaissent dans le nombre maximal de films de la catégorie.
WITH ActorFilmCount AS (
SELECT
AS category_name,
c.name
a.actor_id,|| ' ' || a.last_name AS actor_name,
a.first_name COUNT(fa.film_id) AS film_count
FROM
actor aJOIN
ON a.actor_id = fa.actor_id
film_actor fa JOIN
ON fa.film_id = f.film_id
film f JOIN
ON f.film_id = fc.film_id
film_category fc JOIN
category c ON fc.category_id = c.category_id
GROUP BY
c.name, a.actor_id, a.first_name, a.last_name
),
AS (
RankedActors SELECT
category_name,
actor_name,
film_count,DENSE_RANK() OVER (PARTITION BY category_name ORDER BY film_count DESC) AS rank
FROM
ActorFilmCount
)SELECT
category_name,
actor_name,
film_countFROM
RankedActorsWHERE
rank = 1
ORDER BY
category_name;
On peut se passer des fonctions fenêtres.
WITH ActorFilmCount AS (
SELECT
AS category_name,
c.name
a.actor_id,|| ' ' || a.last_name AS actor_name,
a.first_name COUNT(fa.film_id) AS film_count
FROM
actor aJOIN
ON a.actor_id = fa.actor_id
film_actor fa JOIN
ON fa.film_id = f.film_id
film f JOIN
ON f.film_id = fc.film_id
film_category fc JOIN
category c ON fc.category_id = c.category_id
GROUP BY
c.name, a.actor_id, a.first_name, a.last_name
),
AS (
MaxPerCat SELECT
category_name,MAX(film_count) as m_film_count
FROM
ActorFilmCountGROUP BY
category_name
),
SELECT
ac.category_name,
ac.actor_id,
ac.actor_name,
ac.film_countFROM
ActorFilmCount acJOIN
MaxPerCat mpcUSING(category_name)
WHERE
=mpc.m_film_count
ac.film_countORDER BY
ac.category_name ;
ou encore
WITH ActorFilmCount AS (
SELECT
AS category_name,
c.name
a.actor_id,|| ' ' || a.last_name AS actor_name,
a.first_name COUNT(fa.film_id) AS film_count
FROM
actor aJOIN
ON a.actor_id = fa.actor_id
film_actor fa JOIN
ON fa.film_id = f.film_id
film f JOIN
ON f.film_id = fc.film_id
film_category fc JOIN
category c ON fc.category_id = c.category_id
GROUP BY
c.name, a.actor_id, a.first_name, a.last_name
)
SELECT
*
afc.FROM
ActorFilmCount afcWHERE
>= ALL (
afc.film_count SELECT
a.film_countFROM
ActorFilmCount aWHERE
= afc.category_name
a.category_name ) ;
- Lister sans doublons les paires d’acteurs qui n’ont jamais joué dans un même film.
WITH ActorPairs AS (
-- Generate all possible actor pairs (excluding pairing an actor with themselves)
SELECT
AS actor1_id,
a1.actor_id AS actor2_id,
a2.actor_id || ' ' || a1.last_name AS actor1_name,
a1.first_name || ' ' || a2.last_name AS actor2_name
a2.first_name FROM
actor a1JOIN
ON a1.actor_id < a2.actor_id -- Ensure actor1_id < actor2_id to avoid duplicate pairs
actor a2
),AS (
ActorsTogether -- Find all actor pairs who have acted together in at least one film
SELECT
AS actor1_id,
fa1.actor_id AS actor2_id
fa2.actor_id FROM
film_actor fa1JOIN
ON fa1.film_id = fa2.film_id
film_actor fa2 WHERE
< fa2.actor_id -- Same condition to avoid duplicates
fa1.actor_id
),AS (
ActorsNotTogether -- Left JOIN all possible pairs with the pairs that acted together
SELECT
ap.actor1_name,
ap.actor2_nameFROM
ActorPairs apLEFT JOIN
at ON ap.actor1_id = at.actor1_id AND ap.actor2_id = at.actor2_id
ActorsTogether WHERE
at.actor1_id IS NULL -- Only return pairs that do not exist in the ActorsTogether result
)-- Final result
SELECT
actor1_name,
actor2_nameFROM
ActorsNotTogetherORDER BY
actor1_name, actor2_name;
- Lister les acteurs (prénom, nom) qui ont joué dans des films tournés dans au moins deux langues différentes (langue de tournage :
original_language_id
)
SELECT
a.actor_id,|| ' ' || a.last_name AS actor_name,
a.first_name DISTINCT l.name, ', ') AS languages
STRING_AGG(FROM
actor aJOIN
ON a.actor_id = fa.actor_id
film_actor fa JOIN
ON fa.film_id = f.film_id
film f JOIN
ON f.original_language_id = l.language_id
language l GROUP BY
a.actor_id, a.first_name, a.last_nameHAVING
COUNT(DISTINCT f.original_language_id) > 1
ORDER BY
actor_name;
- Pour chaque magasin (désigné par
store_id
), chaque langue, donnez le nombre de DVDs (physiques) disponibles dans cette langue, dans ce magasin.
SELECT
s.store_id,AS language_name,
l.name COUNT(i.inventory_id) AS inventory_count
FROM
store s
JOIN
ON s.store_id = i.store_id
inventory i JOIN
ON i.film_id = f.film_id
film f JOIN
ON f.language_id = l.language_id
language l WHERE
IS NOT NULL
f.language_id GROUP BY
s.store_id, l.nameORDER BY
s.store_id, l.name;