~/sql-playground ☕ 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

How to use the SQL playground

From a blank page to a query result, chart or export in four 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.

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

A free online SQL playground that runs real SQLite

SQL Playground is an online SQL editor that runs a real database engine — SQLite 3.45, compiled to WebAssembly by sql.js — inside your browser tab. There is no server behind it: your queries, your .sqlite files and your spreadsheets are processed on your own machine, and nothing is uploaded. That makes it a quick SQL sandbox for learning, a place to test a query before it goes into your app, and a private way to explore a database file or a CSV export someone sent you.

It opens with the Chinook music-store sample already loaded, so you can run a query within seconds. Switch to Northwind, a small Employees database, or Shop — a generated e-commerce database with about 7,700 rows of customers, orders, order lines and reviews, big enough for window functions and charts to be interesting.

What you can do in the playground

Query CSV, JSON and Excel files with SQL

You don't need a database file to start. Drop a .csv, .tsv, .json, .ndjson or Excel file (.xlsx, .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;

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 people 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

This build of SQLite includes JSON functions (including -> and ->>), window functions, CTEs, math functions such as sqrt(), and FTS3/FTS4 full-text search. It does not include FTS5, the R*Tree module, generate_series or a REGEXP function.

Your data: saving, files and limits

Everything runs in this browser tab. Files you open are read with the browser's File API and handed to the in-memory SQLite engine; the page only loads the engine itself (and, when you open a spreadsheet, the SheetJS reader) — your tables never travel with those requests. With keep my databases in this browser ticked, which is the default, a database you change is saved a few seconds later to IndexedDB and listed under recent with its query tabs; the six most recent are kept, untouched samples are not. Your last 50 queries are listed under Query History with their result and duration.

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 — raw GitHub files and most open-data portals do. When importing, 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.

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.

Which SQL it speaks

The engine is SQLite 3.45: 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, but foreign keys only after PRAGMA foreign_keys = ON;. 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 dialect table above lists what differs.

The whole 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. It is free, with no account and no ads.

Practise SQL with 32 exercises

The practice panel has 32 exercises on the sample databases, 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. Hints and solutions are one click away, and your progress stays in this browser.

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 developer tools on this site: the KQL example generator, the regex tester, the notepad and the log explorer.

🔎 KQL Example GeneratorKusto query patterns & cheatsheet 🧩 Regex TesterBuild & debug patterns live 📝 NotepadSyntax-highlighted code editor 📜 Log ExplorerSearch & filter log files fast 🧾 JSON Diff ViewerCompare two JSON documents 🎲 Fake Data GeneratorTest data as CSV, JSON or SQL 📈 Chart MakerTurn a table into a chart