Challenges:
SQL EditorCtrl+Enterto run
Output
โšกRun a query to see resultsCtrl+Enter or click Run

Learn SQL Online โ€“ Quick Guide

What is SQL?

SQL (Structured Query Language) is a programming language used to manage and query relational databases. It allows users to retrieve, insert, update, and delete data efficiently using commands like SELECT, JOIN, and GROUP BY.

How to Practice SQL Online?

  1. Open an interactive SQL playground (like the one above)
  2. Browse the available tables in the Schema panel
  3. Write SQL queries โ€” SELECT, JOIN, GROUP BY, and more
  4. Press Ctrl+Enter to execute and see results instantly
  5. Practice with real-world SQL practice problems regularly

Best Ways to Learn SQL Fast

Practice SQL Online with a Real Database

The fastest way to improve at SQL is to actually write queries โ€” not just read about them. This free SQL playground runs a live SQLite engine directly in your browser, backed by 8 interconnected tables and over 1,370 rows of realistic e-commerce data covering users, orders, products, reviews, coupons, and more.

Unlike static tutorials where you read a query and move on, an interactive SQL editor forces you to think through the problem, type the query, and see the real results. You catch mistakes immediately, try different approaches side by side, and build the muscle memory that comes from repetition. That loop โ€” write, run, observe, adjust โ€” is the same one professional data analysts and engineers use every day.

No installation. No account required to start. No cloud credits. Open the editor, write SQL, and press Ctrl+Enter to run.

SQLite engine8 tables ยท 1,370+ rowsInstant executionNo setup neededFree foreverInterview-ready datasetsSave your queries

SQL Practice Questions with Answers

The best way to lock in SQL concepts is to solve real problems on real data. Below are eight progressively harder SQL practice questions โ€” each with a working SQL answer you can copy directly into the playground above and run immediately.

1. List all users from a specific city

A foundational WHERE filter. Almost every real-world report starts with filtering a table by one or more column values.

SELECT first_name, last_name, email, city
FROM   users
WHERE  city = 'New York'
ORDER BY last_name;

2. Count total orders per customer

Introduces GROUP BY and COUNT() โ€” two of the most frequently tested SQL concepts in analyst interviews.

SELECT   u.first_name || ' ' || u.last_name AS customer,
         COUNT(o.id)                          AS total_orders
FROM     users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id
ORDER BY total_orders DESC;

3. Top 5 best-selling products by units sold

Aggregation across multiple tables using JOIN and SUM(). Common in product analytics and e-commerce reporting.

SELECT   p.name,
         SUM(oi.quantity)                               AS units_sold,
         ROUND(SUM(oi.quantity * oi.unit_price), 2)    AS revenue
FROM     products p
JOIN     order_items oi ON oi.product_id = p.id
GROUP BY p.id
ORDER BY units_sold DESC
LIMIT    5;

4. Average order value by month

Date functions and time-series aggregation โ€” essential for any data analyst role.

SELECT   strftime('%Y-%m', created_at) AS month,
         COUNT(*)                       AS orders,
         ROUND(AVG(total), 2)           AS avg_order_value
FROM     orders
GROUP BY month
ORDER BY month;

5. Customers who have never placed an order

The classic LEFT JOIN โ€ฆ WHERE NULL anti-join pattern. This appears in virtually every SQL coding test.

SELECT u.first_name, u.last_name, u.email
FROM   users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE  o.id IS NULL;

6. Customers with lifetime value above the overall average

Subquery inside HAVING โ€” tests whether you understand the difference between filtering rows and filtering groups.

SELECT   u.first_name || ' ' || u.last_name AS customer,
         ROUND(SUM(o.total), 2)              AS lifetime_value
FROM     users u
JOIN     orders o ON o.user_id = u.id
GROUP BY u.id
HAVING   SUM(o.total) > (SELECT AVG(total) FROM orders)
ORDER BY lifetime_value DESC;

7. Product category revenue tiers using CASE

SELECT category,
       ROUND(SUM(oi.quantity * oi.unit_price), 2) AS revenue,
       CASE
         WHEN SUM(oi.quantity * oi.unit_price) > 50000 THEN 'High'
         WHEN SUM(oi.quantity * oi.unit_price) > 20000 THEN 'Medium'
         ELSE 'Low'
       END AS tier
FROM   products p
JOIN   order_items oi ON oi.product_id = p.id
GROUP BY category
ORDER BY revenue DESC;

8. Rolling 30-day order count per customer (window function)

Window functions with OVER and frame specification โ€” the advanced skill that separates junior from senior SQL practitioners.

SELECT u.first_name || ' ' || u.last_name AS customer,
       o.created_at,
       COUNT(*) OVER (
         PARTITION BY o.user_id
         ORDER BY     o.created_at
         ROWS BETWEEN 29 PRECEDING AND CURRENT ROW
       ) AS orders_last_30_days
FROM  orders o
JOIN  users u ON u.id = o.user_id
ORDER BY customer, o.created_at;

SQL Queries for Beginners โ€” Step by Step

If you are just starting out, focus your first week on the five core clauses:SELECT, FROM, WHERE, ORDER BY, and LIMIT. Every advanced query is built on top of these.

SELECT โ€” choose which columns to return

-- Return every column
SELECT * FROM products;

-- Return only specific columns
SELECT name, price, category FROM products;

-- Create a computed column with AS
SELECT name, price * 1.1 AS price_with_tax FROM products;

WHERE โ€” filter rows by condition

-- Products under $50
SELECT name, price
FROM   products
WHERE  price < 50;

-- Multiple conditions with AND
SELECT name, price, category
FROM   products
WHERE  price < 50
  AND  category = 'Electronics';

ORDER BY and LIMIT โ€” sort and paginate

-- 10 most recent orders
SELECT id, user_id, total, created_at
FROM   orders
ORDER BY created_at DESC
LIMIT  10;

GROUP BY with aggregate functions

-- How many users are registered in each city?
SELECT   city, COUNT(*) AS user_count
FROM     users
GROUP BY city
ORDER BY user_count DESC;

Once these feel comfortable, move on to SQL JOINs โ€” the most important concept in relational SQL. A JOIN combines rows from two tables using a shared key, almost always a foreign-key ID column.

-- Basic INNER JOIN: orders with their customer's name
SELECT o.id, u.first_name || ' ' || u.last_name AS customer, o.total
FROM   orders o
JOIN   users u ON u.id = o.user_id
ORDER BY o.created_at DESC
LIMIT  20;

Advanced SQL Practice Problems

Once basic queries are second nature, the next level covers multi-table joins, correlated subqueries, window functions, and CTEs. These are the topics that separate a competent SQL user from a strong one โ€” and the topics that come up repeatedly in senior analyst and engineer interviews.

Multi-table JOINs

Joining three or more tables is routine in production analytics. The key is to follow the foreign-key chain and give every table a short, clear alias.

-- Order line details with customer name, product name, and subtotal
SELECT u.first_name || ' ' || u.last_name       AS customer,
       p.name                                    AS product,
       oi.quantity,
       oi.unit_price,
       ROUND(oi.quantity * oi.unit_price, 2)     AS line_total
FROM   orders o
JOIN   users u        ON u.id       = o.user_id
JOIN   order_items oi ON oi.order_id = o.id
JOIN   products p     ON p.id       = oi.product_id
ORDER BY o.id, line_total DESC;

Correlated Subqueries

A correlated subquery references a column from the outer query, so it re-evaluates once per outer row. Use these to compare each row against a group-level aggregate.

-- Products priced above their own category average
SELECT name, category, price
FROM   products p
WHERE  price > (
  SELECT AVG(price)
  FROM   products
  WHERE  category = p.category
)
ORDER BY category, price DESC;

Window Functions

Window functions compute values across a partition of rows without collapsing them. They are essential for rankings, running totals, and period-over-period comparisons.

-- Rank products by revenue within each category
SELECT name,
       category,
       ROUND(SUM(oi.quantity * oi.unit_price), 2)          AS revenue,
       RANK() OVER (
         PARTITION BY category
         ORDER BY    SUM(oi.quantity * oi.unit_price) DESC
       )                                                     AS rank_in_category
FROM   products p
JOIN   order_items oi ON oi.product_id = p.id
GROUP BY p.id
ORDER BY category, rank_in_category;

Common Table Expressions (CTEs)

CTEs โ€” written with the WITH keyword โ€” let you break a complex query into named, readable steps. They are the professional standard for multi-stage analytical SQL.

WITH customer_totals AS (
  SELECT   user_id,
           COUNT(*)  AS order_count,
           SUM(total) AS lifetime_value
  FROM     orders
  GROUP BY user_id
),
ranked AS (
  SELECT user_id, order_count, lifetime_value,
         NTILE(4) OVER (ORDER BY lifetime_value DESC) AS quartile
  FROM   customer_totals
)
SELECT u.first_name || ' ' || u.last_name AS customer,
       r.order_count,
       ROUND(r.lifetime_value, 2)          AS ltv,
       r.quartile
FROM   ranked r
JOIN   users u ON u.id = r.user_id
ORDER BY r.quartile, r.lifetime_value DESC;

SQL aggregation practice โ€” GROUP BY with HAVING

-- Cities where average customer lifetime value exceeds $500
SELECT   u.city,
         COUNT(DISTINCT u.id)      AS customers,
         ROUND(AVG(totals.ltv), 2) AS avg_ltv
FROM     users u
JOIN (
  SELECT user_id, SUM(total) AS ltv
  FROM   orders
  GROUP BY user_id
) totals ON totals.user_id = u.id
GROUP BY u.city
HAVING   AVG(totals.ltv) > 500
ORDER BY avg_ltv DESC;

SQL subquery examples โ€” EXISTS vs IN

-- Users who have left at least one product review (using EXISTS)
SELECT first_name, last_name, email
FROM   users u
WHERE  EXISTS (
  SELECT 1 FROM reviews r WHERE r.user_id = u.id
)
ORDER BY last_name;

-- Same result using IN (simpler, fine for small result sets)
SELECT first_name, last_name, email
FROM   users
WHERE  id IN (SELECT DISTINCT user_id FROM reviews)
ORDER BY last_name;

SQL Interview Questions for Practice

SQL interviews at data analyst, data engineer, and backend developer levels follow a predictable pattern. Here are the problem types you will see most often โ€” each with a ready-to-run practice query.

Find the Nth highest value

A classic screener. Interviewers want to see whether you knowLIMIT/OFFSET, a subquery approach, orDENSE_RANK().

-- 3rd highest order value using DENSE_RANK (handles ties correctly)
WITH ranked_orders AS (
  SELECT id, total,
         DENSE_RANK() OVER (ORDER BY total DESC) AS rnk
  FROM  orders
)
SELECT * FROM ranked_orders WHERE rnk = 3;

Detect duplicate records

-- Emails registered more than once (data quality check)
SELECT   email, COUNT(*) AS occurrences
FROM     users
GROUP BY email
HAVING   COUNT(*) > 1;

Month-over-month revenue change

-- Monthly revenue with delta vs previous month using LAG()
WITH monthly AS (
  SELECT strftime('%Y-%m', created_at)  AS month,
         ROUND(SUM(total), 2)           AS revenue
  FROM   orders
  GROUP BY month
)
SELECT month,
       revenue,
       LAG(revenue) OVER (ORDER BY month)                        AS prev_month,
       ROUND(revenue - LAG(revenue) OVER (ORDER BY month), 2)   AS change
FROM   monthly;

SQL JOIN practice โ€” different join types

-- INNER JOIN: only rows that match on both sides
SELECT u.email, o.id AS order_id, o.total
FROM   users u
JOIN   orders o ON o.user_id = u.id;   -- same as INNER JOIN

-- LEFT JOIN: all users, even those with no orders
SELECT u.email, COUNT(o.id) AS order_count
FROM   users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id;

Pivot data with conditional aggregation (CASE)

-- Order count by status shown as separate columns
SELECT
  SUM(CASE WHEN status = 'Pending'   THEN 1 ELSE 0 END) AS pending,
  SUM(CASE WHEN status = 'Shipped'   THEN 1 ELSE 0 END) AS shipped,
  SUM(CASE WHEN status = 'Delivered' THEN 1 ELSE 0 END) AS delivered,
  SUM(CASE WHEN status = 'Cancelled' THEN 1 ELSE 0 END) AS cancelled
FROM orders;

SQL GROUP BY practice โ€” cohort retention

-- First-order month for every customer (acquisition cohort)
SELECT u.first_name || ' ' || u.last_name AS customer,
       strftime('%Y-%m', MIN(o.created_at)) AS first_order_month
FROM   users u
JOIN   orders o ON o.user_id = u.id
GROUP BY u.id
ORDER BY first_order_month;

Why Use This SQL Playground

There are dozens of SQL practice platforms available online. Here is what makes this one worth bookmarking for your daily practice and interview preparation.

โšก

Zero friction to start

No account, no download, no waiting. Open the page and run your first query in under 10 seconds.

๐Ÿ—„๏ธ

Realistic relational schema

8 tables modelled on a real e-commerce business โ€” users, orders, products, reviews, coupons, and addresses.

๐Ÿ†

Guided challenge track

Structured problems at beginner, intermediate, and advanced levels with automatic correctness checking.

๐Ÿ’พ

Save your query library

Sign in to name and save queries permanently. Build a personal SQL snippet collection as you learn.

๐Ÿ“ฑ

Works on mobile

Tabbed layout on phones lets you switch between schema browser, editor, and output โ€” no desktop required.

๐Ÿ”

Live schema browser

See every table, column, and type in the left panel instantly โ€” no documentation to dig through.

How it compares to other options

FeatureSQLabGeneric online IDELocal database setup
No installation neededโœ“Often requires signupโœ—
Pre-loaded realistic datasetโœ“ 8 tablesUsually emptyManual setup
Guided challengesโœ“Rarely includedโœ—
Save queries freeโœ“Paid tierโœ“
Mobile-friendly layoutโœ“Often broken on mobileโœ—
Instant query executionโœ“Often slow to spin upโœ“

Frequently Asked Questions

How do I practice SQL online for free?

Open this page and start typing in the editor above โ€” no sign-up or payment needed. The playground runs a real SQLite engine against a pre-loaded database with over 1,370 rows across 8 tables. Press Ctrl+Enter to run any query and see results instantly. For structured practice, use the Challenges bar to pick a problem at your level and receive automatic feedback on your solution.

What SQL dialect does this playground use?

The engine is SQLite, which supports the full standard SQL feature set: SELECT, JOIN, GROUP BY, HAVING, subqueries, CTEs (WITH), and window functions including ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, and NTILE. The main differences from MySQL or PostgreSQL are date functions (strftime instead ofDATE_FORMAT) and the absence of stored procedures. For interview prep and learning core SQL, the syntax transfers directly to every major database system.

How should I prepare for a SQL coding interview?

Make sure you can write the following from memory without hints:

  • INNER, LEFT, RIGHT, and FULL OUTER JOINs โ€” and when to use each
  • GROUP BY with COUNT, SUM, AVG, MIN, MAX
  • HAVING vs WHERE (HAVING filters groups after aggregation; WHERE filters rows before)
  • Correlated subqueries and EXISTS
  • Window functions: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD
  • CTEs (WITH clause) to break complex logic into readable steps
  • The Nth highest value pattern using DENSE_RANK or OFFSET
  • Finding duplicates and handling NULL values correctly

Use the Challenges section here to practice under light time pressure. Aim to solve easy problems in under 2 minutes and medium problems in under 5.

What is the difference between WHERE and HAVING in SQL?

WHERE filters individual rows before any grouping takes place. HAVING filters groups after GROUP BY has aggregated the data. A practical rule: if your filter references an aggregate function like COUNT(), SUM(), or AVG(), you need HAVING. If you are filtering on a plain column value, use WHERE.

-- WHERE: filter rows before grouping
SELECT city, COUNT(*) FROM users WHERE status = 'active' GROUP BY city;

-- HAVING: filter groups after aggregation
SELECT city, COUNT(*) FROM users GROUP BY city HAVING COUNT(*) > 10;
Can I practice MySQL or PostgreSQL queries here?

The engine is SQLite, but roughly 95% of what you write here works identically in MySQL, PostgreSQL, and SQL Server. The main differences to be aware of: date and time functions vary slightly between engines, and some PostgreSQL-specific features (like array types or JSON operators) are not available. For most interview scenarios and day-to-day analytics work, practising on SQLite gives you everything you need to hit the ground running in any environment.

How do I use this platform to prepare for a data analyst interview?

Data analyst SQL interviews typically test three problem types:

  1. Aggregation and reporting โ€” GROUP BY, SUM, COUNT, AVG across time periods and segments
  2. Funnel and retention analysis โ€” tracking users across events using JOINs and window functions
  3. Data cleaning โ€” handling NULLs, deduplication, type casting, and string manipulation

Work through all the beginner and intermediate challenges first. Then tackle advanced problems and time yourself. Companies at the analyst level routinely ask window function and CTE questions, so make sure those feel comfortable before your interview.

What tables and data are available in this SQL sandbox?

The database contains 8 tables modelled on a realistic e-commerce platform:

  • users โ€” customers with name, email, city, and status
  • orders โ€” purchase records with total, status, and timestamps
  • order_items โ€” line items linking orders to products with quantity and price
  • products โ€” catalogue with name, price, category, and stock level
  • categories โ€” product category hierarchy
  • reviews โ€” product ratings and written feedback from customers
  • coupons โ€” discount codes with usage limits and expiry dates
  • addresses โ€” shipping and billing addresses linked to users

Click any table name in the Schema panel on the left to insert a sample SELECT query into the editor.

Is this SQL playground suitable for complete beginners?

Yes. The default query that loads when you open the editor is a real-world example combining JOIN, GROUP BY, and ORDER BY โ€” you can study it, run it, and modify it immediately without writing anything from scratch. The Schema panel shows every table and column so you always know what data is available. A good beginner exercise: run the default query, then try changing the LIMIT value, removing the WHERE clause, or swapping ORDER BY to a different column. Experimenting with a working query is one of the fastest paths into SQL for absolute beginners.