Analyze a Music Store Database with SQL
Query a real music store database to extract business insights with SQL.
Introduction
30 Second Summary
Every music streaming service, online store, and playlist app sits on top of a database. The people who can pull answers from that data are the ones who drive decisions about what to stock, who to target, and where revenue is leaking.
In this project, you will set up a local database environment and write SQL queries against a real music store database to answer business questions about customers, sales, and inventory.
What You'll Build
You will query an 11-table digital music store database using progressively complex SQL, from simple lookups to multi-table JOINs with aggregations.
By the end of this project, you'll have:
- A working SQLite setup on your machine with a real music store database loaded.
- A collection of SQL queries progressing from simple SELECTs to multi-table JOINs with aggregations.
- Answers to 5+ business questions about top customers, genre popularity, and employee sales performance.
- Secret Mission: Write a query that ranks each country's customers by total spending and identifies the top customer per country.
Are there any prerequisites?
None. This project includes full setup instructions for every tool you need. You do not need any prior SQL experience or database software installed.
Set Up SQLite on Windows
To query a music store database, you first need a tool that can read and run SQL commands against it. SQLite is a lightweight database engine that runs entirely from your command line. No servers, no accounts, no background services.
In this step, you will install SQLite on your Windows machine, configure it so you can run it from any folder, and load a pre-built music store database called Chinook.
In this step, get ready to:
- Install SQLite and add it to your system PATH.
- Verify SQLite runs from the command line.
- Download and open the Chinook sample database.
Download and install SQLite
SQLite does not have a traditional installer. You download a ZIP file, extract the tools into a folder, and tell Windows where to find them.
- Check whether SQLite is already on your machine by opening Command Prompt.
- Press the Windows key to open your search bar.
- Type Command Prompt and press Enter to open it.
- Run this version check command:
sqlite3 --version
✔️ I see version 3.53.2 or higher
Great, SQLite is already installed and up to date. Skip ahead to the "Download the Chinook database" substep below.
ⓧ I see an older version
You have an older version of SQLite installed. You need version 3.53.2 for this project. Follow the steps below to download and replace it.
- Open your browser and go to the SQLite download page: https://sqlite.org/download.html.
- Under Precompiled Binaries for Windows, download the file named sqlite-tools-win-x64-3530200.zip (approximately 6.25 MB).
- Open File Explorer and navigate to C:\sqlite. If this folder does not exist, create it now.
- Extract the contents of the ZIP file into C:\sqlite, replacing any existing files.
- Confirm you see sqlite3.exe inside C:\sqlite.
Your PATH should already include C:\sqlite from the previous installation. Close all Command Prompt windows and open a new one, then re-run sqlite3 --version to confirm version 3.53.2.
ⓧ Command not found
SQLite is not installed yet. Follow these steps to set it up from scratch.
- Open your browser and go to the SQLite download page: https://sqlite.org/download.html.
- Under Precompiled Binaries for Windows, download the file named sqlite-tools-win-x64-3530200.zip (approximately 6.25 MB).
- Open File Explorer and create a new folder called C:\sqlite.
- Extract the contents of the ZIP file into C:\sqlite.
- Confirm you see sqlite3.exe inside the folder.
Now add C:\sqlite to your system PATH so you can run sqlite3 from any directory:
- Press the Windows key and search for Edit the system environment variables. Press Enter.
- In the System Properties window, click Environment Variables.
- Under User variables, select Path and click Edit.
- Click New.
- Type C:\sqlite and press Enter.
- Click OK on all open dialogs to save your changes.
What is PATH?
PATH is a list of folders that Windows searches when you type a command. By adding C:\sqlite to PATH, you can type sqlite3 in any Command Prompt window and Windows will find the program automatically.
Verify your SQLite installation
After changing your PATH, existing Command Prompt windows will not pick up the change. You need a fresh window.
- Close all open Command Prompt windows.
- Press the Windows key, type Command Prompt, and press Enter to open a new window.
- Verify your installation by running this command:
sqlite3 --version
You should see 3.53.2 displayed as part of the version output.
Still seeing 'not recognized'?
Command Prompt windows opened before the PATH change will not recognize sqlite3. Make sure you closed all Command Prompt windows and opened a completely new one.
If it still does not work, double-check that sqlite3.exe exists inside C:\sqlite and that your PATH entry is exactly C:\sqlite with no extra spaces or trailing slashes.
As a last resort, restart your computer to force Windows to reload all environment variables.
help me fix the sqlite3 not recognized error on Windows
Download the Chinook database
The Chinook database is a sample music store with 11 interconnected tables. It contains real music data sourced from an iTunes Library, with fictional customer and employee records.
- Download the pre-built database file from this URL: https://github.com/lerocha/chinook-database/releases/download/v1.4.5/Chinook_Sqlite.sqlite.
- Save the file to C:\sqlite\Chinook_Sqlite.sqlite.
- Open the database in SQLite by running this command:
sqlite3 C:\sqlite\Chinook_Sqlite.sqlite
What does this command do?
The sqlite3 command starts the SQLite command-line interface. Passing a file path as an argument tells SQLite to open that specific database.
You are now inside the SQLite shell. Your prompt changes to sqlite> which means SQLite is ready to accept commands.
- Verify the database loaded correctly by listing all tables:
.tables
You should see 11 tables listed: Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track.
Not seeing any tables?
If .tables returns nothing, the file path to Chinook_Sqlite.sqlite may be wrong. SQLite silently creates an empty database if the file does not exist at the path you specified.
Exit SQLite by typing .quit and verify the file exists at C:\sqlite\Chinook_Sqlite.sqlite in File Explorer. Then re-run the sqlite3 command with the correct path.
help me fix the empty .tables output in SQLite
Your SQLite environment is ready and the music store database is loaded. Next up, you will explore the database schema to understand how all 11 tables connect to each other.
Explore the Database Schema
You confirmed the Chinook database has 11 tables in the previous step. Before you can ask meaningful questions of the data, you need to understand how those tables are structured.
A database schema is the blueprint of your data. It tells you what columns each table has, what type of data they hold, and how tables relate to each other. In this step, you'll read the blueprints and then pull out real data with your first SELECT queries.
In this step, get ready to:
- Examine the structure of key tables using the .schema command.
- Run your first SELECT queries to retrieve and count real data.
Examine table structures
The .schema command shows you the CREATE TABLE statement that defines a table. This reveals every column name, its data type, and any constraints like primary keys or foreign keys.
- View the Artist table's structure by running this command in your SQLite prompt:
.schema Artist
What does this output show?
You should see a CREATE TABLE statement listing two columns: ArtistId (a unique numeric identifier) and Name (the artist's name). This is the simplest table in the database.
- View the Album table's structure by running:
.schema Album
What is a foreign key?
The Album table has three columns: AlbumId, Title, and ArtistId. That ArtistId column is a foreign key. It connects each album back to the artist who made it.
Foreign keys are how relational databases link tables together. Later, you'll use these connections to combine data from multiple tables with JOIN queries.
- View the Track table's structure by running:
.schema Track
- View the Customer table's structure by running:
.schema Customer
What should I see?
The Track table has many columns including TrackId, Name, AlbumId, MediaTypeId, GenreId, Composer, Milliseconds, Bytes, and UnitPrice. Notice how it has foreign keys to Album, MediaType, and Genre.
The Customer table has fields like CustomerId, FirstName, LastName, Email, Country, and a SupportRepId foreign key linking each customer to their support employee.
Seeing no output or an error?
Make sure you type the table name with the correct capitalization. Table names in Chinook use PascalCase (e.g., Artist not artist). If you see nothing, the table name did not match any table in the database.
Also confirm the dot command starts with a period and has no space before it. The correct command is .schema with a dot at the very start of the line.
Still stuck? Help me troubleshoot why .schema isn't returning results in my SQLite session.
Run your first queries
Now that you know the table structures, it's time to retrieve actual data. First, you'll switch the output format so results are easier to read.
- Set up readable output formatting by running these two commands, one at a time:
.mode column
.headers on
What do these commands do?
By default, SQLite separates columns with pipe characters (|). The .mode column command aligns results into neat columns instead.
The .headers on command adds column names as a header row above your results. Together, these make query output much easier to read.
- Retrieve the first 10 artists by running:
SELECT * FROM Artist LIMIT 10;
You should see a neatly formatted table with two columns: ArtistId and Name. The 10 rows show artists like AC/DC, Accept, and Aerosmith.
What does this query do?
The SELECT * part means "give me all columns." FROM Artist tells SQLite which table to pull from. LIMIT 10 caps the results at 10 rows so the output stays manageable.
- Query only specific columns from the Customer table by running:
SELECT FirstName, LastName, City, Country FROM Customer;
Why select specific columns?
Instead of SELECT * (all columns), this query names only four columns. This is useful when a table has many columns but you only care about a few. The Customer table has 13 columns, but you only need names and locations right now.
- Count the total number of tracks in the database by running:
SELECT COUNT(*) FROM Track;
You should see a single number representing the total number of rows in the Track table.
What is COUNT(*)?
The COUNT(*) function is an aggregate function. Instead of returning individual rows, it collapses them into a single summary value. You'll use more aggregate functions like SUM and AVG in later steps.
Seeing an error after your SELECT?
The most common mistake is a missing semicolon at the end. Every SQL statement must end with ; in the SQLite CLI. If you forgot it, SQLite shows a ...> prompt waiting for more input. Type ; and press Enter to finish the statement.
Also check for typos in table or column names. They must match the schema exactly (e.g., FirstName not firstname).
Need more help? Help me fix a SQL syntax error in my SQLite query.
You now know the shape of the data you're working with. Next up, you'll use WHERE and ORDER BY to filter and sort your results, pulling out exactly the data you need.
Filter and Sort Data
You have a database loaded with thousands of rows of music data. Retrieving everything at once is overwhelming and unhelpful.
In this step, you will use WHERE to filter rows down to specific conditions, and ORDER BY to sort results in a meaningful order.
In this step, get ready to:
- Filter rows using WHERE conditions.
- Sort results using ORDER BY with ASC and DESC.
- Combine filtering and sorting in a single query.
Filter rows with WHERE
The WHERE clause lets you specify conditions that rows must meet to appear in your results. Only rows that satisfy the condition are returned.
- Find all customers from Brazil by running this query:
SELECT FirstName, LastName, City FROM Customer WHERE Country = 'Brazil';
You should see a list of customers with their first names, last names, and cities, all located in Brazil.
What does WHERE do here?
The WHERE Country = 'Brazil' condition checks every row in the Customer table. Only rows where the Country column exactly matches the text 'Brazil' are included in the output.
String values in SQL must be wrapped in single quotes. Column names and keywords do not need quotes.
- Find tracks longer than 5 minutes (300000 milliseconds) by running:
SELECT Name, Milliseconds FROM Track WHERE Milliseconds > 300000 LIMIT 10;
You should see 10 track names with their duration in milliseconds, all longer than 300000.
What is LIMIT?
LIMIT 10 caps the output to 10 rows. Without it, this query would return hundreds of results.
For numeric comparisons, you do not use quotes around the value. > means "greater than", and you can also use <, >=, <=, and =.
- Combine two conditions with AND by running:
SELECT Name, UnitPrice FROM Track WHERE UnitPrice > 0.99 AND Milliseconds < 200000;
You should see tracks that are both more expensive than $0.99 and shorter than 200000 milliseconds.
What does AND do?
AND requires both conditions to be true for a row to appear. If you want rows matching either condition, you would use OR instead.
Seeing a syntax error?
Check that your string values are wrapped in single quotes ('Brazil' not "Brazil"). Double quotes are for identifiers in SQLite, not for text values.
Make sure every query ends with a semicolon. Without it, the SQLite CLI waits for more input instead of executing.
Help me fix a WHERE clause syntax error in SQLite.
Sort results with ORDER BY
Filtering narrows which rows appear. Sorting controls the order they appear in. ORDER BY arranges your results by a column, either ascending or descending.
- List the 10 longest tracks by running:
SELECT Name, Milliseconds FROM Track ORDER BY Milliseconds DESC LIMIT 10;
You should see 10 tracks listed from the longest to the shortest duration.
What do DESC and ASC mean?
DESC sorts from highest to lowest (descending). ASC sorts from lowest to highest (ascending). If you leave off the direction, SQL defaults to ASC.
- List customers alphabetically by last name by running:
SELECT LastName, FirstName, Country FROM Customer ORDER BY LastName ASC;
You should see all customers sorted alphabetically from A to Z by their last name.
- Combine filtering and sorting together by running:
SELECT Name, Milliseconds FROM Track WHERE Milliseconds > 300000 ORDER BY Milliseconds DESC LIMIT 5;
You should see only 5 tracks, all longer than 5 minutes, sorted from longest to shortest.
How do WHERE, ORDER BY, and LIMIT work together?
SQL processes the clauses in a specific order. WHERE filters first, then ORDER BY sorts the filtered results, then LIMIT caps the final output.
Think of it as a pipeline: start with all rows, narrow them down, arrange them, then take the top few.
Results look unsorted?
Make sure ORDER BY comes after WHERE but before LIMIT. The clause order matters in SQL.
If you accidentally put LIMIT before ORDER BY, you will get an error because SQL expects a specific keyword sequence.
Help me fix an ORDER BY issue in my SQL query.
You can now pinpoint exactly which rows you want and control how they appear. Next up, you will group rows together and calculate totals using aggregate functions.
Aggregate Data with GROUP BY
In the previous step, you filtered rows with WHERE and sorted results with ORDER BY. Now it's time to answer bigger questions about the data as a whole.
Business questions like "which genre sells the most?" or "how much did each country spend?" require you to group rows together and calculate summaries. SQL's GROUP BY clause combined with aggregate functions makes this possible in a single query.
In this step, get ready to:
- Use aggregate functions like COUNT, SUM, and AVG to summarize groups of data.
- Filter grouped results with HAVING to find groups that meet specific thresholds.
Use aggregate functions
Aggregate functions collapse many rows into a single summary value. COUNT counts rows, SUM adds up values, and AVG calculates the mean.
When you pair an aggregate function with GROUP BY, SQL splits the rows into groups first, then runs the function on each group separately.
- Count how many tracks each album has by running this query in your SQLite session:
SELECT AlbumId, COUNT(*) AS TrackCount FROM Track GROUP BY AlbumId ORDER BY TrackCount DESC LIMIT 10;
What does this query do?
GROUP BY AlbumId gathers all tracks that share the same AlbumId into one group. Then COUNT(*) counts how many tracks are in each group.
The AS TrackCount part gives the count column a readable name. Without it, the column header would just say COUNT(*).
You should see a table with two columns showing the top 10 albums ranked by number of tracks.
- Find the total invoice amount per country by running:
SELECT BillingCountry, SUM(Total) AS TotalSales FROM Invoice GROUP BY BillingCountry ORDER BY TotalSales DESC;
What does this query do?
SUM(Total) adds up every invoice's Total value within each country group. The result is a ranked list of countries by total revenue.
You should see every billing country listed with its total sales amount, sorted from highest to lowest.
- Find the average track length per genre by running this query:
SELECT g.Name AS Genre, AVG(t.Milliseconds) AS AvgLength FROM Track t INNER JOIN Genre g ON t.GenreId = g.GenreId GROUP BY g.Name ORDER BY AvgLength DESC LIMIT 5;
What is INNER JOIN doing here?
The Track table stores genre information as a numeric GenreId, not a readable name. The Genre table maps each GenreId to a human-friendly name like "Rock" or "Jazz".
INNER JOIN Genre g ON t.GenreId = g.GenreId connects each track row to its matching genre row. This lets you group by genre name instead of a meaningless number.
The t and g are short aliases for the Track and Genre tables. They save you from typing the full table name every time you reference a column.
You should see five genres listed with their average track lengths in milliseconds, with the longest-average genre at the top.
Seeing an error about ambiguous column names?
When two joined tables have columns with the same name, you must prefix the column with the table alias (e.g., t.GenreId not just GenreId). Double-check that every column reference uses the correct alias.
Also make sure the ON keyword connects matching columns from both tables. The join condition must match a column from each side.
Help me fix my INNER JOIN query
Filter groups with HAVING
WHERE filters individual rows before grouping happens. But what if you want to filter after grouping? For example, "only show me genres with more than 100 tracks."
That's where HAVING comes in. It works like WHERE, but it filters groups after the aggregation is complete.
- Find genres that have more than 100 tracks by running:
SELECT g.Name AS Genre, COUNT(*) AS TrackCount FROM Track t INNER JOIN Genre g ON t.GenreId = g.GenreId GROUP BY g.Name HAVING TrackCount > 100 ORDER BY TrackCount DESC;
How does HAVING work here?
SQL processes this query in order: first it joins Track and Genre, then groups by genre name, then counts tracks in each group. Only after all that does HAVING TrackCount > 100 filter out the small genres.
If you tried using WHERE TrackCount > 100 instead, the query would fail because TrackCount doesn't exist yet at the WHERE stage.
You should see only the genres that contain more than 100 tracks. Most genres will be filtered out, leaving just the most popular ones.
- Find countries that spent more than $40 total by running:
SELECT BillingCountry, SUM(Total) AS TotalSpent FROM Invoice GROUP BY BillingCountry HAVING TotalSpent > 40 ORDER BY TotalSpent DESC;
What does this query do?
This query sums up all invoice totals per country, then uses HAVING TotalSpent > 40 to keep only the high-spending countries. It answers the question: "Which countries are our biggest markets?"
You should see a short list of countries with their total spending, all above $40.
Query returns no results?
Check that you typed HAVING and not WHERE. Using WHERE on an aggregated column will produce an error or unexpected results.
Also make sure the column alias in HAVING matches the alias you defined with AS exactly. TotalSpent is case-sensitive.
Help me fix my HAVING query
You can now group data, summarize it with aggregate functions, and filter those summaries. Next up, you'll combine multiple tables with JOINs to answer real business questions about customers, employees, and artists.
Join Tables to Answer Business Questions
In the previous step, you grouped and aggregated data to spot patterns across the database. Now you will combine multiple tables in a single query to answer real business questions about the music store.
Each table in the Chinook database holds one piece of the puzzle. Artists live in one table, their albums in another, and individual tracks in a third. To answer a question like "which artist has the most tracks?" you need to connect all three tables together using INNER JOIN.
In this step, get ready to:
- Join three tables (Artist, Album, Track) to find the artists with the most tracks.
- Join Customer and Invoice tables to identify the highest-spending customers.
- Join Employee, Customer, and Invoice tables to compare employee sales performance.
Answer "Which artists have the most tracks?"
The Artist table knows artist names, the Album table links albums to artists, and the Track table links tracks to albums. To count tracks per artist, you need to chain all three together.
- Run this query in your SQLite CLI to join Artist, Album, and Track and rank artists by track count:
SELECT ar.Name AS Artist, COUNT(t.TrackId) AS TrackCount
FROM Artist ar
INNER JOIN Album al ON ar.ArtistId = al.ArtistId
INNER JOIN Track t ON al.AlbumId = t.AlbumId
GROUP BY ar.Name
ORDER BY TrackCount DESC
LIMIT 10;
What does this query do?
This query chains three INNER JOINs together. Each JOIN connects a shared column (a foreign key) between two tables.
- Artist ar joins to Album al on ArtistId because every album belongs to one artist.
- Album al joins to Track t on AlbumId because every track belongs to one album.
- COUNT(t.TrackId) counts how many tracks each artist has across all their albums.
- The aliases ar, al, and t are shorthand names that keep the query readable.
- Check your results. You should see a ranked list of 10 artists with Iron Maiden at the top, followed by U2 and Metallica.
Seeing an error instead of results?
- Check that each ON clause references the correct column from each side of the join. A typo in ArtistId or AlbumId causes a "no such column" error.
- Make sure you typed the aliases consistently. If you wrote FROM Artist ar, every reference to the Artist table must use ar, not Artist.
- Confirm the semicolon at the end of the query. Without it, SQLite waits for more input instead of running the query.
Why isn't my multi-table JOIN returning results?
Answer "Who are the top 5 customers by total spending?"
Customer names live in the Customer table, but their purchase totals are stored in the Invoice table. Joining these two tables lets you sum up each customer's spending.
- Run this query to find the top 5 customers ranked by total spending:
SELECT c.FirstName, c.LastName, c.Country, SUM(i.Total) AS TotalSpent
FROM Customer c
INNER JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY c.CustomerId
ORDER BY TotalSpent DESC
LIMIT 5;
What does this query do?
- Customer c joins to Invoice i on CustomerId because each invoice belongs to one customer.
- SUM(i.Total) adds up all invoice totals for each customer.
- GROUP BY c.CustomerId ensures the sum is calculated per customer, not across the entire table.
- The result shows each customer's name, country, and lifetime spending at the store.
- Check your results. You should see 5 rows with customer names, countries, and spending totals ordered from highest to lowest.
Results look wrong or duplicated?
- If you see duplicate customer names, make sure you are grouping by c.CustomerId (not by c.FirstName). Two customers could share the same first name.
- If spending totals look too low, confirm you used SUM(i.Total) and not COUNT(i.Total). COUNT counts rows, SUM adds up values.
Help me fix my Customer + Invoice JOIN query.
Answer "Which employee made the most sales?"
The Employee table stores staff details, but sales data lives across Customer and Invoice. The connection is the SupportRepId column in Customer, which links each customer to the employee who supports them.
- Run this query to compare employee sales performance:
SELECT e.FirstName, e.LastName, COUNT(i.InvoiceId) AS SalesCount, SUM(i.Total) AS TotalSales
FROM Employee e
INNER JOIN Customer c ON e.EmployeeId = c.SupportRepId
INNER JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY e.EmployeeId
ORDER BY TotalSales DESC;
What does this query do?
- Employee e joins to Customer c on e.EmployeeId = c.SupportRepId. This links each employee to the customers they support.
- Customer c joins to Invoice i on CustomerId, bringing in each customer's invoices.
- COUNT(i.InvoiceId) tells you how many sales (invoices) each employee handled.
- SUM(i.Total) tells you the total revenue each employee generated.
- The combination of both metrics gives a complete picture of each employee's performance.
- Check your results. You should see a short list of employees ranked by total sales revenue, with both their invoice count and revenue total displayed.
Getting zero rows or a missing column error?
- The most common mistake is joining on the wrong column. The link between Employee and Customer is e.EmployeeId = c.SupportRepId, not e.EmployeeId = c.CustomerId.
- If you see "ambiguous column name", add the table alias before every column reference (e.FirstName, not just FirstName).
Help me debug my Employee sales query.
Why use aliases like ar, al, t, c, i, and e?
When you join multiple tables, every column reference needs to specify which table it belongs to. Writing Artist.ArtistId every time makes queries long and hard to read.
Short aliases (ar for Artist, al for Album, t for Track) keep your queries compact. Pick aliases that remind you of the original table name so you can read the query at a glance.
Secret mission
Rank Customers by Country
Real businesses need to know their most valuable customer in every market. Can you write a single query that identifies the top spender in each country from the Chinook database? This challenge introduces subqueries and more complex multi-table logic.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- SQLite installation at C:\sqlite (includes sqlite3.exe and other tools).
- Chinook database file at C:\sqlite\Chinook_Sqlite.sqlite.
- System PATH entry pointing to C:\sqlite.
Keep everything running
No action needed. Choose this if you plan to keep practicing SQL or want to come back to the Chinook database for more queries.
- Your SQLite installation stays in C:\sqlite and remains available from any Command Prompt window.
- The Chinook database file takes up about 1 MB of disk space.
- Nothing is running in the background. SQLite only uses resources when you actively open it.
Pause - I'll come back to this later
Close any open Command Prompt windows to free up memory. Your files stay on disk for when you return.
- If you still have the SQLite CLI open, type .quit and press Enter to exit.
- Close any Command Prompt windows you opened during this project.
- When you want to resume, open a new Command Prompt and run:
sqlite3 C:\sqlite\Chinook_Sqlite.sqlite
You will be right back where you left off.
Delete - I don't want to use this again
Remove all project files and undo the PATH change to fully clean up your system.
- If you still have the SQLite CLI open, type .quit and press Enter to exit.
- Delete the entire C:\sqlite folder by running this command in Command Prompt:
rmdir /s /q C:\sqlite
- Remove the PATH entry: open Start, search environment variables, click Edit the system environment variables.
- Click Environment Variables.
- Under User variables, select Path and click Edit.
- Find the C:\sqlite entry, select it, and click Delete.
- Click OK on all open dialogs to save the change.
Nice Work!
Nice Work!
Nice work! You just queried a real music store database using SQL, progressing from simple SELECTs all the way to multi-table JOINs with aggregations.
Here's what you accomplished:
- Set up a local SQLite environment and loaded the Chinook database with 11 interconnected tables of real music store data.
- Filtered, sorted, and aggregated data using WHERE, ORDER BY, GROUP BY, and aggregate functions like COUNT, SUM, and AVG.
- Joined multiple tables with INNER JOIN to answer real business questions like identifying top-spending customers and highest-performing employees.
- Secret Mission: Wrote a complex subquery to rank customers by country and identify the top spender in each one.
Ready to quiz yourself?