Bookshops, books and availability¶
Problem¶
A database of books (each of a genre), bookshops, and the availability of each book in each bookshop, with the quantity in stock. Write four queries.
Schema and test data¶
CREATE TABLE Libri (
codice INT PRIMARY KEY,
titolo TEXT NOT NULL,
id_genere INT NOT NULL,
prezzo_di_copertina DECIMAL(10, 2),
FOREIGN KEY (id_genere) REFERENCES Generi(id)
);
CREATE TABLE Generi (
id INT PRIMARY KEY,
nome TEXT NOT NULL,
descrizione TEXT
);
CREATE TABLE Librerie (
id INT PRIMARY KEY,
indirizzo TEXT NOT NULL,
città TEXT NOT NULL
);
CREATE TABLE Disponibilita (
libreria INT NOT NULL,
libro INT NOT NULL,
quantità INT NOT NULL,
PRIMARY KEY (libreria, libro),
FOREIGN KEY (libreria) REFERENCES Librerie(id),
FOREIGN KEY (libro) REFERENCES Libri(codice)
);
-- Test data (not part of the exercise): a bookshop with two cheap books, an empty
-- bookshop, and quantities that make "number of titles" and "number of copies" differ.
INSERT INTO Generi VALUES (1, 'Romanzo', NULL), (2, 'Saggio', NULL), (3, 'Giallo', NULL);
INSERT INTO Libri VALUES (1, 'Il nome della rosa', 1, 14.50), (2, 'Sapiens', 2, 22.00),
(3, 'La donna della domenica', 3, 11.00), (4, 'Il Gattopardo', 1, 18.00);
INSERT INTO Librerie VALUES (1, 'Via Roma 1', 'Milano'), (2, 'Corso Italia 5', 'Milano'),
(3, 'Via Po 10', 'Torino'), (4, 'Piazza Dante 2', 'Napoli'); -- 4: empty
INSERT INTO Disponibilita VALUES
(1, 1, 3), (1, 3, 2), (1, 2, 1),
(2, 1, 10), (2, 4, 1),
(3, 2, 4), (3, 3, 20), (3, 4, 2);
The schema is the one given in the exercise; the test data are mine.
Queries¶
Query 1 · Bookshops with a book under 20¶
ID, address and city of the bookshops that have at least one book with a cover price below 20, ordered by bookshop ID.
SELECT C1.id, C1.indirizzo, C1.città
FROM Librerie AS C1
JOIN Disponibilita AS C2
ON C1.id = C2.libreria
JOIN Libri AS C3
ON C2.libro = C3.codice
WHERE C3.prezzo_di_copertina < 20
| id | indirizzo | città |
|---|---|---|
| 1 | Via Roma 1 | Milano |
| 1 | Via Roma 1 | Milano |
| 2 | Corso Italia 5 | Milano |
| 2 | Corso Italia 5 | Milano |
| 3 | Via Po 10 | Torino |
| 3 | Via Po 10 | Torino |
Duplicates and order
A bookshop appears once for every cheap book it stocks: each of the three appears twice
in the test data. SELECT DISTINCT fixes it, and the requested ORDER BY C1.id is
missing.
Query 2 · Books above 12 available in Milan¶
Code, title, cover price and genre name of the books with a cover price above 12 available in the bookshops of Milan, ordered by book code.
SELECT DISTINCT C1.codice, C1.titolo, C1.prezzo_di_copertina, C4.nome
FROM Libri AS C1
JOIN Disponibilita AS C2
ON C1.codice = C2.libro
JOIN Librerie AS C3
ON C2.libreria = C3.id
JOIN Generi AS C4
ON C1.id_genere = C4.id
WHERE C1.prezzo_di_copertina > 12 AND C3.città = 'Milano'
| codice | titolo | prezzo_di_copertina | nome |
|---|---|---|---|
| 1 | Il nome della rosa | 14.5 | Romanzo |
| 2 | Sapiens | 22 | Saggio |
| 4 | Il Gattopardo | 18 | Romanzo |
DISTINCT is used correctly here: a book stocked by two Milan bookshops would otherwise
appear twice. The requested ORDER BY C1.codice is missing.
Query 3 · Books available in each bookshop¶
For each bookshop: ID, address, city and the number of books it has, showing 0 for a bookshop with none; ordered by ID.
SELECT C1.id, C1.indirizzo, C1.città, COUNT(C2.quantità) AS numero_libri
FROM Librerie AS C1
LEFT JOIN Disponibilita AS C2
ON C1.id = C2.libreria
GROUP BY C1.id, C1.indirizzo, C1.città
ORDER BY C1.id
| id | indirizzo | città | numero_libri |
|---|---|---|---|
| 1 | Via Roma 1 | Milano | 3 |
| 2 | Corso Italia 5 | Milano | 2 |
| 3 | Via Po 10 | Torino | 3 |
| 4 | Piazza Dante 2 | Napoli | 0 |
Query 4 · The bookshop with the most books¶
ID, address and city of the bookshop (or bookshops) with the most books, ordered by ID.
CREATE VIEW quantita_libri_view AS
SELECT C1.id AS id_libreria, C1.indirizzo, C1.città, COUNT(C2.quantità) AS quantita_libri
FROM Librerie AS C1
JOIN Disponibilita AS C2
ON C1.id = C2.libreria
GROUP BY id_libreria, C1.indirizzo, C1.città;
SELECT id_libreria, indirizzo, città
FROM quantita_libri_view
WHERE quantita_libri = (SELECT MAX(quantita_libri) FROM quantita_libri_view)
| id_libreria | indirizzo | città |
|---|---|---|
| 1 | Via Roma 1 | Milano |
| 3 | Via Po 10 | Torino |
Titles or copies?
COUNT(C2.quantità) counts the titles a bookshop stocks (one row of
Disponibilita per title). If "number of books" means the copies in stock, the
count must be SUM(C2.quantità) (with COALESCE(..., 0) for the empty bookshop in
Query 3). The two readings give different answers on the test data: by titles, the
bookshops 1 and 3 tie with 3 each; by copies, bookshop 3 wins with 26 against 11 and 6.
Takeaways¶
- A join from the "one" side to the "many" side repeats rows:
DISTINCTwhen only the "one" side is wanted. COUNTcounts rows,SUMadds values: the question decides which one.