How to Learn SQL for Free: A Practice-First Plan With Real Queries
Free courses, practice sites, and sample databases to learn SQL step by step, from SELECT and JOIN to CTEs and window functions.

SQL is the language you use to ask a database questions. In the 2025 Stack Overflow Developer Survey, 58.6% of all respondents said they used SQL in the past year. Only JavaScript and HTML/CSS scored higher. The good news for anyone learning it: the best material is free, the databases are free, and the practice data is free. This guide lists what we'd use, in what order, and shows the queries running on a real database.
Can you learn SQL for free?
Yes. Free, browser-based courses like SQLBolt, Khan Academy, and Harvard's CS50 SQL cover everything from a first SELECT to window functions, and SQLite and DuckDB give you a free database on your own laptop. You pay nothing unless you want a certificate.
SQL is an open standard. The current version is ISO/IEC 9075-1:2023, published in June 2023. Every major database (PostgreSQL, MySQL, SQLite, SQL Server, BigQuery) follows most of that standard, so what you learn on one carries over to the others with small changes. We cover those changes in the dialect section below.
The one thing money buys is a certificate. CS50's SQL course is free to take through its OpenCourseWare page, and a verified certificate comes only through edX. Codecademy's Learn SQL is marked Free on its course page, and the certificate of completion is in the paid tier. The lessons are the same either way.
What order should you learn SQL in?
Learn SQL in this order: SELECT and FROM, then WHERE and ORDER BY, then GROUP BY with COUNT and SUM, then JOIN, then subqueries and CTEs, then window functions. Each step reuses the one before it, and every free course in this guide follows about the same path.
To show what that path looks like, we ran each step against the Chinook database. Chinook is a free sample database that models a digital music store, with scripts for SQLite, PostgreSQL, MySQL, SQL Server, Oracle, and DB2. The SQLite file is about 1 MB. These are its tables:
Album Customer Genre InvoiceLine Playlist TrackArtist Employee Invoice MediaType PlaylistTrack
Step one is picking columns from a table and limiting how many rows come back:
SELECT NameFROM ArtistLIMIT 5;
╭───────────────────╮│ Name │╞═══════════════════╡│ AC/DC ││ Accept ││ Aerosmith ││ Alanis Morissette ││ Alice In Chains │╰───────────────────╯
Step two filters rows with WHERE and sorts them with ORDER BY. Here we find tracks longer than ten minutes and convert milliseconds to minutes in the SELECT:
SELECT Name, Milliseconds / 60000.0 AS minutesFROM TrackWHERE Milliseconds > 600000ORDER BY Milliseconds DESCLIMIT 5;
╭─────────────────────────────┬────────────────────╮│ Name │ minutes │╞═════════════════════════════╪════════════════════╡│ Occupation / Precipice │ 88.115883333333329 ││ Through a Looking Glass │ 84.813966666666673 ││ Greetings from Earth, Pt. 1 │ 49.338216666666668 ││ The Man With Nine Lives │ 49.2833 ││ Battlestar Galactica, Pt. 2 │ 49.268016666666668 │╰─────────────────────────────┴────────────────────╯
Step three groups rows and counts them. GROUP BY collapses the 59 customers into one row per country, and HAVING filters on the count after grouping:
SELECT Country, COUNT(*) AS customer_countFROM CustomerGROUP BY CountryHAVING COUNT(*) > 5ORDER BY customer_count DESC;
╭─────────┬────────────────╮│ Country │ customer_count │╞═════════╪════════════════╡│ USA │ 13 ││ Canada │ 8 │╰─────────┴────────────────╯
The HAVING clause exists because WHERE runs before grouping. If you try to filter on the aggregate inside WHERE, SQLite stops you:
SELECT CustomerId, SUM(Total) AS total_spentFROM InvoiceWHERE total_spent > 20GROUP BY CustomerId;
Parse error near line 3: misuse of aggregate: SUM()
Step four joins two tables on a shared key. Album has an ArtistId column that points at Artist, so the join pairs each album with its artist:
SELECT Album.Title, Artist.Name AS artistFROM Album JOIN Artist ON Album.ArtistId = Artist.ArtistIdORDER BY Album.AlbumIdLIMIT 5;
╭───────────────────────────────────────┬───────────╮│ Title │ artist │╞═══════════════════════════════════════╪═══════════╡│ For Those About To Rock We Salute You │ AC/DC ││ Balls to the Wall │ Accept ││ Restless and Wild │ Accept ││ Let There Be Rock │ AC/DC ││ Big Ones │ Aerosmith │╰───────────────────────────────────────┴───────────╯
Step five is a CTE, a common table expression. It names a query with WITH so you can use it like a table in the next query. This one sums revenue per country, then picks the top three:
WITH country_revenue AS ( SELECT BillingCountry AS country, ROUND(SUM(Total), 2) AS revenue FROM Invoice GROUP BY BillingCountry)SELECT country, revenue FROM country_revenueORDER BY revenue DESC LIMIT 3;
╭─────────┬─────────╮│ country │ revenue │╞═════════╪═════════╡│ USA │ 523.06 ││ Canada │ 303.96 ││ France │ 195.1 │╰─────────┴─────────╯
Step six is window functions. RANK() OVER (PARTITION BY Country ...) ranks customers inside their own country without collapsing rows the way GROUP BY does. Here we keep the top spender per country:
WITH totals AS ( SELECT c.CustomerId, c.Country, c.FirstName || ' ' || c.LastName AS customer, ROUND(SUM(i.Total), 2) AS total_spent FROM Customer c JOIN Invoice i USING (CustomerId) GROUP BY c.CustomerId), ranked AS ( SELECT *, RANK() OVER (PARTITION BY Country ORDER BY total_spent DESC) AS country_rank FROM totals)SELECT Country, customer, total_spent, country_rankFROM ranked WHERE country_rank = 1ORDER BY Country LIMIT 5;
╭───────────┬─────────────────┬─────────────┬──────────────╮│ Country │ customer │ total_spent │ country_rank │╞═══════════╪═════════════════╪═════════════╪══════════════╡│ Argentina │ Diego Gutiérrez │ 37.62 │ 1 ││ Australia │ Mark Taylor │ 37.62 │ 1 ││ Austria │ Astrid Gruber │ 42.62 │ 1 ││ Belgium │ Daan Peeters │ 37.62 │ 1 ││ Brazil │ Luís Gonçalves │ 39.62 │ 1 │╰───────────┴─────────────────┴─────────────┴──────────────╯
Once these six steps feel normal, the rest of SQL is details: data types, NULL handling, and the differences between databases. Our guide to SQL data types covers the first of those.
Which free SQL courses are worth your time?
For a complete beginner, SQLBolt teaches the basics in the browser with no signup, Khan Academy's Intro to SQL adds video, and CS50 SQL from Harvard goes deepest for free. Kaggle's Intro to SQL is the pick if you want to learn on BigQuery, a real cloud warehouse.
Course | Cost | Database it teaches on | Format | Length stated by the course |
|---|---|---|---|---|
Free | SQLite-style, in-browser | Interactive lessons and exercises | Not stated | |
Free | In-browser | Videos plus interactive challenges | Not stated | |
Free; certificate via edX is paid | SQLite, plus PostgreSQL and MySQL later | Lectures, problem sets, final project | Seven weeks of material | |
Free | Google BigQuery | Notebook tutorials and exercises | 3 hours | |
Free lessons; certificate is paid | In-browser | Interactive lessons | 5 hours | |
Free | Mode's own editor | 15 basic, 20 intermediate, 9 advanced lessons, plus 8 analytics lessons | Not stated | |
Free | PostgreSQL in a terminal | Project-based, uses Bash and VS Code | Not stated | |
Free | MySQL | Single YouTube video | 4 hours 20 minutes | |
Free | MySQL, SQL Server, PostgreSQL, Oracle, Access | Reference pages with Try It editor | Not stated | |
Free, login optional | In-browser | Wiki-style tutorial with exercises | Not stated |
A few notes on picking. SQLBolt is the fastest way to write your first query, because the lessons start with parts of a SELECT and the exercises run on the same page. CS50 SQL is the only one on the list with graded problem sets and a final project; submitting them needs a free edX account. freeCodeCamp's Relational Database course is the odd one out: it teaches PostgreSQL through a terminal alongside Bash, and freeCodeCamp says that section is no longer being updated.
The freeCodeCamp video course has over 20 million views and runs 4 hours and 20 minutes. It's a good background lecture, but a video alone won't build the habit of typing queries. Pair it with one of the interactive options.
Where can you practice SQL for free?
For free SQL practice, use LeetCode's SQL 50 study plan for interview-style problems, PostgreSQL Exercises for a single realistic dataset that goes up to window functions and recursive queries, and HackerRank's SQL domain for graded problems sorted by topic. All three run in the browser with no install.
Practice site | Cost | Database | What you get |
|---|---|---|---|
Free study plan | Several engines to choose from | 50 SQL questions built as an interview prep plan | |
Free (content is CC BY-SA 3.0) | PostgreSQL | Questions from SELECT and WHERE through joins, aggregation, window functions, and recursive queries, all on one country-club dataset | |
Free | Several engines | Problems grouped into Basic Select, Advanced Select, Aggregation, Basic Join, Advanced Join, and Alternative Queries, tagged Easy to Hard | |
Free | SQL Server (T-SQL) | Write queries against real public Stack Overflow data |
Stack Exchange Data Explorer is the most interesting of the four because the data is real and large. It's an open source tool for running arbitrary queries against public data from every Stack Exchange site, and you can start from queries other people have shared.
When you want to type SQL without setting anything up, browser sandboxes give you an empty database:
Sandbox | Engines available |
|---|---|
SQLite, MariaDB, PostgreSQL, MS SQL | |
SQL Server, SQLite, PostgreSQL, MySQL, MariaDB, Oracle | |
Free fiddles; private fiddles need the paid PRO tier | |
DuckDB, with sample datasets loaded (NYC Taxi in Parquet, Star Trek CSV, train services) |
Which free database should you install to learn SQL?
Install SQLite first, then DuckDB. SQLite is public domain, ships as a single command-line program, and reads the Chinook file used in this article with no server to configure. DuckDB is also a single file, and it can query a CSV directly with no import step.
SQLite is in the public domain, so there's no license to accept. The sqlite3 command-line shell opens a database file or an in-memory one and lets you type queries at a prompt. If you'd rather click than type, DB Browser for SQLite is a free desktop app with a spreadsheet-like view of the tables.
To reproduce the examples above, download the Chinook SQLite file from the project's releases page and open it:
curl -fL https://github.com/lerocha/chinook-database/releases/download/v1.4.5/Chinook_Sqlite.sqlite \ -o Chinook_Sqlite.sqlitesqlite3 Chinook_Sqlite.sqlite
Then turn on headers and the box output style, and every query in this article will print the same way:
.headers on.mode boxSELECT COUNT(*) AS customers FROM Customer;
The DuckDB CLI is a single, dependency-free executable precompiled for Windows, Mac, and Linux. Its best trick for a learner is querying files directly. We exported the Customer table to a CSV from SQLite, then asked DuckDB to group it without creating a table:
SELECT Country, COUNT(*) AS nFROM 'customers.csv'GROUP BY CountryORDER BY n DESCLIMIT 5;
┌─────────┬───────┐│ Country │ n ││ varchar │ int64 │├─────────┼───────┤│ USA │ 13 ││ Canada │ 8 ││ Brazil │ 5 ││ France │ 5 ││ Germany │ 4 │└─────────┴───────┘
That means any CSV you already have (an export from a spreadsheet, a bank statement, a sales report) becomes a practice database in one line.
When you want to practice on a database that runs in the cloud, the free tiers below cost nothing and need no credit card for the BigQuery sandbox:
Free cloud database | What the free tier includes | Watch out for |
|---|---|---|
1 TiB of query processing per month, 10 GiB of storage, no credit card | The 10 GiB storage limit is lifetime and is not refunded when you delete data | |
$0, PostgreSQL, unlimited API requests | Projects pause after 1 week of inactivity; limit of 2 active projects | |
$0, PostgreSQL, 100 projects, 100 compute-hours per project per month | 0.5 GB of storage per project |
BigQuery is the one we'd pick for a learner who wants to see what a data warehouse feels like. Kaggle's Intro to SQL course runs on it, and Google hosts public datasets you can query right away.
Which free datasets can you practice SQL on?
Chinook, Sakila, and Northwind are the classic small sample databases, and the Stack Exchange Data Explorer, BigQuery public datasets, and NYC taxi trip records give you real data at scale. Start small so you can check your answers by hand, then move to the large ones.
Dataset | Size and shape | Where to get it |
|---|---|---|
Chinook | 11 tables of a digital music store: 59 customers, 412 invoices, 3,503 tracks, 275 artists | GitHub releases, scripts for six database engines |
Sakila | MySQL's official sample database, a DVD rental store | |
Northwind | Microsoft's classic trading-company sample | |
Stack Exchange data | Live public data from every Stack Exchange site | data.stackexchange.com, queried in T-SQL |
BigQuery public datasets | Many large public datasets hosted by Google | cloud.google.com/bigquery/public-data, free within the sandbox limits |
NYC taxi trip records | Pickup and drop-off times and locations, distances, fares, payment types, in Parquet files |
The Chinook row counts above come from running COUNT(*) on the SQLite file. Small numbers like these are the point: when a GROUP BY says the USA has 13 customers, you can scroll the Customer table and count them yourself.
Which SQL dialect should you learn first?
Learn on SQLite or PostgreSQL first. SQLite is the smallest install, and PostgreSQL is the most used database in the 2025 Stack Overflow survey at 55.6% of all respondents. Both use standard syntax for the things that differ most between databases: row limits, string joining, and quoting names.
The core of SQL (SELECT, WHERE, GROUP BY, JOIN, CTEs, window functions) is the same everywhere. The differences that catch beginners are in the small stuff:
Task | SQLite / PostgreSQL | MySQL | SQL Server |
|---|---|---|---|
Return the first 5 rows | LIMIT 5 (PostgreSQL also accepts FETCH FIRST 5 ROWS ONLY) | LIMIT 5 |
Join two strings | The double-pipe operator, as in FirstName \ | \ | ' ' \ | \ | LastName | CONCAT(); the double pipe means OR unless the PIPES_AS_CONCAT mode is on | CONCAT() or + |
|---|
Quote a column name | Double quotes | Backticks, unless ANSI_QUOTES mode is on | |
|---|---|---|---|
Today's date | date('now') in SQLite, CURRENT_DATE in PostgreSQL | GETDATE() returns a datetime |
The string concatenation row is the one that bites most often. This query works the same in SQLite and DuckDB:
SELECT FirstName || ' ' || LastName AS full_nameFROM CustomerLIMIT 3;
In MySQL with default settings, the same double pipe is a logical OR, so you'd write CONCAT(FirstName, ' ', LastName) instead. DuckDB accepts both forms and returned the same three names for each.
How long does it take to learn SQL?
The free courses themselves put the basics at a few hours and a full course at a few weeks. Kaggle's Intro to SQL is listed at 3 hours, Codecademy's Learn SQL at 5 hours, and CS50 SQL is seven weeks of material with problem sets.
Those numbers cover the lessons. Getting comfortable comes from repetition on data you care about, which is why the practice sites and datasets above matter more than which course you choose. A workable plan is one interactive course to cover the six steps, then a problem set (LeetCode SQL 50 or PostgreSQL Exercises) until joins and GROUP BY stop needing thought, then one real dataset you explore with no assignment at all.
Where does SQL take you next?
Once you can write these queries, the next question is where the data you want to query is. At most companies it's spread across a CRM, a payments tool, a support desk, and a few spreadsheets. The usual fix is to copy all of it into one warehouse (BigQuery, Snowflake, Redshift, or Databricks) and point your SQL at that. Our guide on data centralization without a dedicated data team walks through that setup.
The loading step is what data teams call ELT: extract from the source, load into the warehouse, then transform with SQL. Our ETL vs ELT comparison explains why the transform moved to the end, and why that made SQL the main skill for working with warehouse data. Tools like Metabase then let you turn those queries into dashboards.
Erathos is the loading layer in that stack. It connects to your sources and delivers the tables into your warehouse, so the SQL you just learned has something real to run against. Try Erathos free for 14 days.