~/sql-playground fake data json diff kql ☕ Support me apps ← about me

SQL Playground β€” run SQLite online, in your browser

Write and run real SQL on sample databases or your own .sqlite, CSV, JSON or Excel files Β· syntax highlighting, autocomplete, charts & a schema diagram Β· nothing is uploaded
✎ Edit tables in a spreadsheet-style grid, or learn with 32 SQL practice exercises that check your answer.
β–Έ Database no database loaded
loading sqlite wasm...
β–Έ SQL practice Β· 32 exercises
  1. Beginner
  2. βœ“1Highest earners firstemployees
  3. βœ“2Former employeesemployees
  4. βœ“3Hired recentlyemployees
  5. βœ“4Headcount per departmentemployees
  6. βœ“5Average salaryemployees
  7. βœ“6Employees with their departmentemployees
  8. βœ“7Albums by one artistchinook
  9. βœ“8Stock problemsnorthwind
  10. βœ“9Orders by statusshop
  11. βœ“10Cheap teashop
  12. Intermediate
  13. βœ“11Big-budget salariesemployees
  14. βœ“12Above averageemployees
  15. βœ“13Small departmentsemployees
  16. βœ“14Tracks per genrechinook
  17. βœ“15The three longest trackschinook
  18. βœ“16Never soldchinook
  19. βœ“17Revenue per countrychinook
  20. βœ“18Order totalsnorthwind
  21. βœ“19Repeat customersnorthwind
  22. βœ“20Unsold productsnorthwind
  23. βœ“21Best sellersshop
  24. βœ“22Revenue in 2024shop
  25. βœ“23Silent customersshop
  26. βœ“24First order from Belgiumshop
  27. βœ“25Price riseshop
  28. Advanced
  29. βœ“26Average order value per channelshop
  30. βœ“27Running totalshop
  31. βœ“28Month-over-month changeshop
  32. βœ“29Best seller per categoryshop
  33. βœ“30Ratings per categoryshop
  34. βœ“31Top 10 customersshop
  35. βœ“32Counting with recursionemployees

Practise SQL on real sample data

Pick an exercise. The matching sample database opens in the editor above, in its own tab. Write your query there and press βœ“ check my answer β€” it runs your SQL and a reference solution on a fresh copy of the data and compares the results, so any correct query passes, not just one exact answer.

They start with SELECT, WHERE and ORDER BY, move on to joins, GROUP BY, HAVING and subqueries, and end with CTEs, window functions and recursive queries. Your progress is remembered in this browser.

How to use the SQL playground

From a blank page to a query result, chart or export in five steps β€” no install, no account, nothing uploaded.

1Pick your data

Load a sample (Chinook, Northwind, Employees or Shop), drop your own .sqlite, CSV, JSON or Excel file, or start an empty database.

2Write a query

Autocomplete knows your tables and columns. Stuck? πŸ“š examples has ready-made queries for every sample.

3Run and read

⌘/Ctrl + Enter runs the script; each result gets a tab you can sort, chart, or check with 🧭 plan.

4Keep or share

Export CSV, JSON, Markdown, Excel or INSERTs, download the .sqlite, or πŸ”— share a link that reruns the query.

5Practise

Open the SQL practice panel: 32 exercises from a first SELECT to recursive CTEs, checked by comparing your result with a reference answer.

WRITEA real SQL editor

Comfortable for one-liners and long scripts alike.

Autocomplete
tables, columns, alias. suggestions
Format
one clause per line, keywords upper-cased
Tabs
several queries side by side
Errors
β€œdid you mean…?” and dialect hints
DATABring your own

Files become real SQLite tables you can join.

Files
.sqlite .db .csv .tsv .json .xlsx .ods
URL
fetch a file, or link with ?url=
Types
INTEGER, REAL or TEXT detected per column
Grid
edit rows, add or rename columns visually
UNDERSTANDSee what happens

Tools that explain a database and a query.

Schema
tables, views, indexes, row counts
Diagram
ER diagram drawn from foreign keys
Plan
EXPLAIN QUERY PLAN as a tree
Chart
bar, line or area, download as SVG
LEARNPractise SQL

32 exercises checked against a reference answer.

Levels
first SELECT to recursive CTEs
Checking
any correct query passes
Help
hints and solutions on request
Progress
saved in this browser

SQL playground online, explained

SQL Playground is an online SQL editor that runs a real database engine inside your browser tab: SQLite 3.45.2, compiled to WebAssembly by sql.js 1.10.3. There is no database server behind it. Your queries, your .sqlite files and your spreadsheets are processed on your own machine and are never uploaded. Use it as a SQL sandbox for learning, to test a query before it goes into your app, or to explore a database file or CSV export someone sent you without installing anything.

It opens with the Chinook music-store sample already loaded, so your first query can run within seconds of the page loading.

What runs your queries

The engine is the official SQLite C library compiled to WebAssembly by the open-source sql.js project. The page loads two files from cdnjs.cloudflare.com: sql-wasm.js (about 50 KB) and sql-wasm.wasm (about 650 KB). After that every statement runs locally. Run SELECT sqlite_version(); to check the version yourself; the first query tab does exactly that.

The database lives in memory. Opening a file reads the whole thing into RAM, and changes stay there until you download the database or the playground saves it in your browser (see below). This build includes JSON functions (with -> and ->>), window functions, CTEs, math functions such as sqrt(), STRICT tables, RETURNING, FULL OUTER JOIN and FTS3/FTS4 full-text search. It does not include FTS5, the R*Tree module, generate_series or a REGEXP function, so those statements fail with "no such module" or "no such function".

What you can do in the playground

The sample databases

The four samples are created by SQL inside the page, so they load instantly and work offline once the engine is loaded. They are small on purpose: you can see every row and check an answer by eye.

SampleTablesRowsGood for
Chinook7 tables (artists, albums, tracks, genres, customers, invoices, invoice_items) + a top_customers view83joins across a chain of foreign keys
Northwind6 tables (categories, suppliers, products, customers, orders, order_details)71order totals, HAVING, LEFT JOIN for missing rows
Employees2 tables (departments, employees) + 1 index20first SELECTs, grouping, subqueries
Shop5 tables (customers 320, products 29, orders 2,600, order_items 4,056, reviews 700) + a monthly_revenue view + 3 indexes7,705window functions, CTEs, charts, query plans

Chinook and Northwind are trimmed-down versions of the well-known teaching databases, not the full downloads: Chinook here has 19 tracks rather than several thousand. Shop is generated in your browser from a fixed random seed, so everyone gets exactly the same 7,705 rows and the exercises always have the same answers.

Query CSV, JSON and Excel files with SQL

You don't need a database file to start. Drop a .csv, .tsv, .txt, .json, .ndjson/.jsonl or Excel file (.xlsx, .xlsm, .xls, .ods) onto the page, or paste a URL and press fetch. The importer detects the delimiter and column types, shows a preview, and creates a real table; for workbooks you pick the sheet. Import several files and JOIN them in one query, then export the answer. Values with leading zeros, such as postcodes and product codes, stay text so nothing is silently changed.

  1. Drop the file on the page (or use β‡ͺ import).
  2. Check the table name and the detected column types, then press βœ“ import table.
  3. Query it: SELECT * FROM my_file WHERE amount > 100 ORDER BY amount DESC;

A column becomes INTEGER if every value is a whole number, REAL if all are numeric and TEXT otherwise. Postcodes, phone numbers and values over 15 digits stay TEXT, empty cells become NULL, and Excel dates arrive as YYYY-MM-DD text that sorts correctly. Untick detect column types to keep everything as text, or fix a column later with CAST. Excel files need the SheetJS reader, which is only downloaded the first time you open a spreadsheet.

Export formats

A result can be saved as CSV, JSON (an array of objects), a Markdown table, an Excel-ready CSV (UTF-8 with a byte-order mark and semicolons, so Excel opens it with accents and columns intact) or a file of INSERT statements for a table name you choose. You can also copy the grid and paste it into a spreadsheet. A chart downloads as SVG, and ⬇ db saves the whole database as a standard .sqlite file that opens in DB Browser for SQLite, the sqlite3 command line or any SQLite library.

Example SQL queries to try

Each example opens the right sample database and runs the query, so you can change it and see what happens.

TechniqueQuery
Filter and sortSELECT name, category, price FROM products WHERE price > 25 ORDER BY price DESC;β–Ά run
Join two tablesSELECT al.Title, ar.Name AS artist FROM albums al JOIN artists ar ON ar.ArtistId = al.ArtistId;β–Ά run
Group and aggregateSELECT country, COUNT(*) AS customers FROM customers GROUP BY country ORDER BY customers DESC;β–Ά run
Filter groups with HAVINGSELECT CustomerID, COUNT(*) AS orders FROM orders GROUP BY CustomerID HAVING COUNT(*) > 1;β–Ά run
SubquerySELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);β–Ά run
Common table expressionWITH totals AS (SELECT order_id, SUM(quantity * unit_price) AS value FROM order_items GROUP BY order_id) SELECT ROUND(AVG(value), 2) AS avg_order FROM totals;β–Ά run
Window function: running totalSELECT month, revenue, ROUND(SUM(revenue) OVER (ORDER BY month), 2) AS running_total FROM monthly_revenue;β–Ά run
Window function: rankSELECT name, dept_id, salary, RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank_in_dept FROM employees;β–Ά run
DatesSELECT strftime('%Y', order_date) AS year, COUNT(*) AS orders FROM orders GROUP BY year;β–Ά run
CASE expressionSELECT name, price, CASE WHEN price < 10 THEN 'budget' WHEN price < 50 THEN 'mid' ELSE 'premium' END AS tier FROM products;β–Ά run
JSON functionsSELECT json_object('name', name, 'salary', salary) AS doc FROM employees LIMIT 5;β–Ά run
Recursive CTEWITH RECURSIVE d(day) AS (SELECT date('2026-01-01') UNION ALL SELECT date(day, '+1 day') FROM d WHERE day < '2026-01-07') SELECT day, strftime('%w', day) AS weekday FROM d;β–Ά run

SQLite cheat sheet

The statements you reach for most often, in SQLite syntax. Paste any of them into an empty database to try them.

TaskSQL
Create a tableCREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT NOT NULL, born DATE);
Insert rowsINSERT INTO people (name, born) VALUES ('Ada', '1815-12-10'), ('Alan', '1912-06-23');
Update rowsUPDATE people SET name = 'Ada Lovelace' WHERE id = 1;
UpsertINSERT INTO people (id, name) VALUES (1, 'Ada') ON CONFLICT(id) DO UPDATE SET name = excluded.name;
Delete rowsDELETE FROM people WHERE born < '1900-01-01';
Return changed rowsDELETE FROM people WHERE id = 2 RETURNING *;
Add a columnALTER TABLE people ADD COLUMN email TEXT;
Index a columnCREATE INDEX idx_people_name ON people(name);
List tablesSELECT name FROM sqlite_master WHERE type = 'table';
Describe a tablePRAGMA table_info(people);
Today / nowSELECT date('now'), datetime('now', 'localtime');
Days between datesSELECT julianday('2026-12-25') - julianday('2026-09-17') AS days;
Concatenate textSELECT 'Hello, ' || 'world' AS greeting;
Aggregate into a listSELECT group_concat(name, ', ') FROM people;
Enforce foreign keysPRAGMA foreign_keys = ON;

SQLite vs MySQL vs PostgreSQL: differences that trip learners up

Most everyday SQL works everywhere, but a few things differ. If you are practising for MySQL or PostgreSQL, these are the spots to watch.

SQLiteMySQLPostgreSQL
Auto-increment keyINTEGER PRIMARY KEYINT AUTO_INCREMENTGENERATED … AS IDENTITY / SERIAL
Current timedatetime('now'), CURRENT_TIMESTAMPNOW()now()
Join stringsa || b, concat(a, b)CONCAT(a, b)a || b, concat(a, b)
PagingLIMIT 10 OFFSET 20 or LIMIT 20, 10LIMIT 10 OFFSET 20 or LIMIT 20, 10LIMIT 10 OFFSET 20
Case-insensitive matchLIKE (ASCII letters)LIKE (default collations)ILIKE
Booleansintegers 0 / 1 (TRUE and FALSE are aliases)TINYINT(1) / BOOLEAN aliasreal BOOLEAN type
Column typesflexible (type affinity), STRICT tables optionalenforcedenforced
Format a datestrftime('%Y-%m', d)DATE_FORMAT(d, '%Y-%m')to_char(d, 'YYYY-MM')
UpsertON CONFLICT(col) DO UPDATEON DUPLICATE KEY UPDATEON CONFLICT (col) DO UPDATE
Aggregate into a stringgroup_concat(x, sep), string_agg(x, sep)GROUP_CONCAT(x SEPARATOR sep)string_agg(x, sep)
FULL OUTER JOINyes (since 3.39)noyes
RETURNINGyes (since 3.35)noyes

The biggest conceptual difference is typing. SQLite uses type affinity: a column declared INTEGER will still accept the text 'abc' unless the table is created STRICT. MySQL and PostgreSQL reject or convert such values. SQLite also has no separate DATE type: dates are stored as ISO text such as 2026-10-13 and handled with date(), datetime(), strftime() and julianday(). And foreign keys are only enforced after PRAGMA foreign_keys = ON;.

Which SQL it speaks

The engine supports joins of every kind including RIGHT and FULL OUTER JOIN, CTEs and WITH RECURSIVE, window functions, RETURNING, upserts, JSON and math functions, PRAGMA and ATTACH. Paging works with LIMIT 10 OFFSET 20 and the MySQL-style LIMIT 20, 10. PRIMARY KEY, NOT NULL, UNIQUE, CHECK and DEFAULT are enforced as usual. PostgreSQL-only syntax such as ILIKE, :: casts and JSONB is not available; the error message suggests the SQLite equivalent. The fundamentals you practise here (SELECT, joins, grouping, subqueries, CTEs, window functions) carry straight over to MySQL and PostgreSQL.

The editor runs as a script: statements execute in order and each one that returns rows gets its own result tab, while β–Ά selection runs only the highlighted part. The layout works on phones and tablets too, though longer queries are nicer with a keyboard.

Saving your work, file sizes and limits

With keep my databases in this browser ticked (the default), a database you have changed is saved a few seconds later to your browser's IndexedDB storage (a database called jb-sql-playground) together with its query tabs, and listed under recent. The six most recent are kept; untouched samples and empty databases are not saved, and databases over 64 MB are not autosaved (download them with ⬇ db). Untick the box and nothing is kept after you close the tab. Your last 50 queries (under Query History), your practice progress and the recent list are kept in localStorage. Clearing your browser's site data removes all of it.

Because SQLite loads the whole database into memory, a few hundred MB is fine on a modern laptop, while multi-gigabyte files are better served by a desktop app such as DB Browser for SQLite. A file fetched from a URL is downloaded by your browser, so the host must allow cross-origin requests (CORS). Raw GitHub files and most open-data portals do.

There is no undo for data: DELETE, DROP TABLE and grid edits apply immediately. Wrap experiments in BEGIN; … ROLLBACK; or download a copy with ⬇ db first. The download keeps the connection open, so settings such as PRAGMA foreign_keys stay as they were. Grid edits run parameterised UPDATE, INSERT and DELETE statements, so a value like O'Brien is stored as text and never executed. Tables created WITHOUT ROWID can be changed with SQL but not in the grid.

Privacy: what stays on your device and what does not

Your tables, files and query results never leave the browser: the page has no server to send them to. It does make a few requests of its own, and you should know what they are:

A πŸ”— share link contains the text of your query in the URL (and the sample name, if you used an unchanged sample) but never your data, so check the SQL itself for anything private before you send it.

Practise SQL with 32 exercises

The practice panel has 32 exercises on the sample databases: 10 beginner, 15 intermediate and 7 advanced, from a first SELECT to window functions and recursive CTEs. Your query is checked by comparing its result with a reference solution on a fresh copy of the data, so any correct approach passes; where the task asks for a sort order, the order is checked too. Hints and solutions are one click away, and your progress stays in this browser. A link such as ?practice=12 opens one exercise directly.

Keyboard shortcuts

⌘/Ctrl + EnterRun the editor
⌘/Ctrl + Shift + EnterRun the selected text only
⌘/Ctrl + Shift + FFormat SQL (the selection, or everything)
⌘/Ctrl + SpaceOpen autocomplete
TabAccept a suggestion, or indent two spaces

More free tools