Spotify Database
Concept. We use three tables in this course: Users, Songs, and Listens. Listens links a user to a song with foreign keys, and records the rating and time.
Intuition. One Listens row reads as a sentence: user 3 (Daffy) played song 2 (Willow) and rated it 4.9. Users and Songs hold the who and what.
The Three Tables
Figure: The Spotify schema: Users, Songs, and Listens, joined by two foreign keysThree tables shown together. Users has a primary key user_id; Songs has a primary key song_id; Listens has a primary key listen_id and two foreign keys, user_id pointing at Users.user_id and song_id pointing at Songs.song_id. One row is decoded first: listen 8 means Daffy (user 3) played Willow (song 2) and rated it 4.9. Then two links are drawn value to value: a box round the 3 in that row's user_id, an arrow to a box round the 3 in Users, and a box round the 2 in its song_id, an arrow to a box round the 2 in Songs. Color key: blue PK and FK badges mark the keys, per-user identity bars color the rows by user.
| user_id | name | |
|---|---|---|
| 1 | Mickey | mickey@example.com |
| 2 | Minnie | minnie@example.com |
| 3 | Daffy | daffy@example.com |
| 4 | Pluto | pluto@example.com |
| song_id | title | artist | genre |
|---|---|---|---|
| 1 | Evermore | Taylor Swift | Pop |
| 2 | Willow | Taylor Swift | Pop |
| 3 | Shape of You | Ed Sheeran | Rock |
| 4 | Photograph | Ed Sheeran | Rock |
| 5 | Shivers | Ed Sheeran | Rock |
| 6 | Yesterday | Beatles | Classic |
| 7 | Yellow Submarine | Beatles | Classic |
| 8 | Hey Jude | Beatles | Classic |
| 9 | Bad Blood | Taylor Swift | Rock |
| 10 | DJ Mix | DJ | NULL |
| listen_id | user_id | song_id | rating | listen_time |
|---|---|---|---|---|
| 1 | 1 | 1 | 4.5 | 2024-08-30 14:35:00 |
| 2 | 1 | 2 | 4.2 | NULL |
| 3 | 1 | 6 | 3.9 | 2024-08-29 10:15:00 |
| 4 | 2 | 2 | 4.7 | NULL |
| 5 | 2 | 7 | 4.6 | 2024-08-28 09:20:00 |
| 6 | 2 | 8 | 3.9 | 2024-08-27 16:45:00 |
| 7 | 3 | 1 | 2.9 | NULL |
| 8 | 3 | 2 | 4.9 | 2024-08-26 12:30:00 |
| 9 | 3 | 6 | NULL | NULL |
Figure 1. All three tables, and the keys that tie them together: each has a primary key (user_id, song_id, listen_id), and Listens carries two foreign keys that point back at the other tables, so one Listens row reads as a sentence: listen 8 is Daffy (user 3) playing Willow (song 2), rated 4.9. Every query in the course joins these three tables along those IDs.
How the Schema Is Defined
Figure: Reading SQL DDL: how the Listens table is definedThe CREATE TABLE listens statement, annotated. Each column names a type: INT for the integer ids, DECIMAL(2,1) for a rating like 4.5, TIMESTAMP for the time. PRIMARY KEY marks listen_id as the unique row id; NOT NULL marks a column required, and rating and listen_time omit it so they may be NULL. Two FOREIGN KEY ... REFERENCES clauses tie user_id to a real Users row and song_id to a real Songs row, so the database rejects a listen that points at a user or song that does not exist. Blue annotations in the margin label the type, the primary key, and the foreign keys.
Figure 2. CREATE TABLE assigns each column a name, a type (INT, DECIMAL(2,1), TIMESTAMP), and constraints. PRIMARY KEY selects the unique row identifier. NOT NULL requires a value. FOREIGN KEY ... REFERENCES constrains user_id and song_id to values that exist in the parent table.
The DDL for all three tables:
Users Table
CREATE TABLE users (
user_id INT PRIMARY KEY, -- Unique ID for each user
name VARCHAR(100) NOT NULL, -- User's name (required)
email VARCHAR(255) UNIQUE NOT NULL -- Email (required, no duplicates)
);
Songs Table
CREATE TABLE songs (
song_id INT PRIMARY KEY, -- Unique ID for each song
title VARCHAR(255) NOT NULL, -- Song title (required)
artist VARCHAR(100) NOT NULL, -- Artist name (required)
genre VARCHAR(50) -- Genre (optional - can be NULL)
);
Listens Table
CREATE TABLE listens (
listen_id INT PRIMARY KEY, -- Unique ID for each listen
user_id INT NOT NULL, -- Which user (required)
song_id INT NOT NULL, -- Which song (required)
rating DECIMAL(2,1), -- Rating 0.0-5.0 (optional)
listen_time TIMESTAMP, -- When played (optional)
FOREIGN KEY (user_id) REFERENCES users(user_id), -- Must be valid user
FOREIGN KEY (song_id) REFERENCES songs(song_id) -- Must be valid song
);
SQL Schema Reference Guide
Data Types
| Type | Meaning | Example |
|---|---|---|
INT |
Whole numbers | 1, 42, 999 |
VARCHAR(n) |
Text up to n characters | 'Taylor Swift' (max 100) |
DECIMAL(p,s) |
Decimal with p total digits, s after decimal | 4.5 (max 9.9) |
TIMESTAMP |
Date and time | 2024-08-30 14:35:00 |
| Modern Types | New & Popular | Used For |
JSONB |
JSON documents (PostgreSQL) | {"playlists": ["workout", "chill"]} |
BLOB |
Binary large objects | Album cover images, audio files |
VECTOR(n) |
AI embeddings | [0.1, -0.3, 0.8, ...] for song similarity |
Constraints
| Constraint | Purpose | Example in Our Database |
|---|---|---|
PRIMARY KEY |
Unique identifier for each row. Can be multiple columns | user_id, song_id, listen_id Or composite: (student_id, class_id) |
UNIQUE |
No duplicates allowed | email (no two users can share) |
NOT NULL |
Required - must have a value | name, title, artist |
FOREIGN KEY |
References PRIMARY KEY in another table | user_id → users, song_id → songs |
| (no constraint) | Optional - can be NULL | genre, rating, listen_time |