A museum: artists, paintings and rooms¶
Problem¶
A museum database: artists, their paintings, and the rooms where paintings are exhibited, each room on a floor. A painting may have no room (for example, when it is in storage). Write four queries.
Schema and test data¶
CREATE TABLE Artisti (
codice text NOT NULL PRIMARY KEY,
cognome text,
nome text);
CREATE TABLE Quadri (
codice integer NOT NULL PRIMARY KEY,
artista text NOT NULL REFERENCES Artisti,
descrizione text NOT NULL,
stanza text REFERENCES Stanze,
anno integer
);
CREATE TABLE Stanze (
nome text NOT NULL PRIMARY KEY,
piano integer NOT NULL
);
-- Test data (not part of the exercise): an empty room on floor 1, a painting in
-- storage (no room), and an artist with two paintings in the same room.
INSERT INTO Artisti VALUES ('CAR', 'Caravaggio', 'Michelangelo'), ('RAF', 'Sanzio', 'Raffaello'),
('TIZ', 'Vecellio', 'Tiziano');
INSERT INTO Stanze VALUES ('Sala A', 1), ('Sala B', 1), ('Sala C', 2), ('Sala D', 1); -- Sala D: empty
INSERT INTO Quadri VALUES
(1, 'CAR', 'Canestra di frutta', 'Sala A', 1599),
(2, 'CAR', 'Bacchino malato', 'Sala A', 1594),
(3, 'RAF', 'Madonna del prato', 'Sala A', 1506),
(4, 'RAF', 'La velata', 'Sala B', 1516),
(5, 'TIZ', 'Venere di Urbino', 'Sala C', 1538),
(6, 'TIZ', 'Flora', NULL, 1515); -- in storage
The schema is the one given in the exercise; the test data are mine.
Queries¶
Query 1 · Paintings on the first floor¶
All the paintings on floor 1, with code and description of the painting and surname and name of the artist.
SELECT C1.codice, C1.descrizione, C3.cognome, C3.nome
FROM Quadri AS C1
JOIN Stanze AS C2
ON C1.stanza = C2.nome
JOIN Artisti AS C3
ON C1.artista = C3.codice
WHERE C2.piano = 1
| codice | descrizione | cognome | nome |
|---|---|---|---|
| 1 | Canestra di frutta | Caravaggio | Michelangelo |
| 2 | Bacchino malato | Caravaggio | Michelangelo |
| 3 | Madonna del prato | Sanzio | Raffaello |
| 4 | La velata | Sanzio | Raffaello |
Query 2 · Rooms on the first floor and how many paintings they show¶
For each room on floor 1: name, floor and number of paintings exhibited (0 if none).
SELECT C1.nome, C1.piano, COUNT(C2.stanza) AS numero_totoale_quadri
FROM Stanze AS C1
LEFT JOIN Quadri AS C2
ON C1.nome = C2.stanza
WHERE C1.piano = 1
GROUP BY C1.nome, C1.piano
| nome | piano | numero_totoale_quadri |
|---|---|---|
| Sala A | 1 | 3 |
| Sala B | 1 | 1 |
| Sala D | 1 | 0 |
The empty Sala D appears with 0 thanks to the LEFT JOIN and to counting C2.stanza
rather than rows.
Query 3 · Works of each artist in each first-floor room¶
For each room on floor 1 that shows at least one painting: name and floor of the room, name and surname of each artist with works there, and how many works of that artist it shows. Solved in two ways.
SELECT C1.nome AS nome_stanza , C1.piano AS piano_stanza, C3.nome AS nome_artista, C3.cognome AS cognome_artista, COUNT(C2.stanza) AS numero_opere_esposte
FROM Stanze AS C1
LEFT JOIN Quadri AS C2
ON C1.nome = C2.stanza
JOIN Artisti AS C3
ON C2.artista = C3.codice
WHERE C1.piano = 1 AND C2.stanza IS NOT NULL
GROUP BY C1.nome, C1.piano, C3.nome, C3.cognome
-- it could also be solved as follows, more directly (without left join and where)
SELECT C1.nome AS nome_stanza , C1.piano AS piano_stanza, C3.nome AS nome_artista, C3.cognome AS cognome_artista, COUNT(C2.stanza) AS numero_opere_esposte
FROM Stanze AS C1
JOIN Quadri AS C2
ON C1.nome = C2.stanza
JOIN Artisti AS C3
ON C2.artista = C3.codice
WHERE C1.piano = 1
GROUP BY C1.nome, C1.piano, C3.nome, C3.cognome
| nome_stanza | piano_stanza | nome_artista | cognome_artista | numero_opere_esposte |
|---|---|---|---|---|
| Sala A | 1 | Michelangelo | Caravaggio | 2 |
| Sala A | 1 | Raffaello | Sanzio | 1 |
| Sala B | 1 | Raffaello | Sanzio | 1 |
Both versions give the same result: since rooms without paintings must be excluded anyway,
the inner join of the second version does directly what the first obtains with
LEFT JOIN plus IS NOT NULL.
Query 4 · Artists with at least two paintings in the same room¶
The (name, surname, room) triples where the room contains at least two paintings of that artist.
SELECT C1.nome AS nome_artista, C1.cognome AS cognome_artista, C3.nome AS nome_stanza
FROM Artisti AS C1
JOIN Quadri AS C2
ON C1.codice = C2.artista
JOIN Stanze AS C3
ON C2.stanza = C3.nome
GROUP BY C1.nome, C1.cognome, C3.nome
HAVING COUNT (C2.codice) >= 2 -- having filters AFTER the grouping (where would not work here, because it filters before)
| nome_artista | cognome_artista | nome_stanza |
|---|---|---|
| Michelangelo | Caravaggio | Sala A |
Takeaways¶
WHEREfilters rows before grouping,HAVINGfilters groups after: a condition on a count can only go inHAVING.- An outer join followed by a filter on the optional side is just an inner join written the long way.
- The
#comments of the original (MySQL syntax) are written here as--, the standard SQL comment, so that the queries run on any database.