SQL Practice Online: 30 Exercises with Answers
The fastest way to learn SQL is to write queries on a real table and check the result. These 30 SQL practice exercises use one small shop database (customers, products, orders, order items and employees). Every answer below was run in SQLite, and its result table is shown, so you can compare your output row by row. Open any query in the free browser playground with one click. No sign-up, nothing to install.
हिन्दी में समझें
यहाँ 30 SQL अभ्यास प्रश्न हैं, आसान से कठिन तक। हर सवाल का उत्तर और उसका परिणाम (result table) दिया गया है। "Run in playground" दबाकर क्वेरी अपने ब्राउज़र में चलाइए। कोई लॉगिन या इंस्टॉल नहीं।
The practice database
All exercises use the same fictional shop. Keep this list open while you write queries.
| Table | Columns | Rows |
|---|---|---|
| customers | id, name, city | 6 |
| products | id, name, category, price | 5 |
| orders | id, customer_id, order_date, status | 8 |
| order_items | order_id, product_id, quantity | 11 |
| employees | id, name, department, salary, manager_id | 6 |
Engine: SQLite 3.49 running in your browser. Queries that change data only affect a temporary copy.
Beginner SQL practice questions
SELECT, WHERE, ORDER BY, LIMIT, DISTINCT, LIKE and calculated columns.
1Show every column and row from the customers table.
customers टेबल की सभी पंक्तियाँ और कॉलम दिखाएँ।
Hint
Use SELECT * with FROM.
Show answer and result
SELECT * FROM customers;| id | name | city |
|---|---|---|
| 1 | Asha | Delhi |
| 2 | Ravi | Mumbai |
| 3 | Neha | Delhi |
| 4 | Kabir | Pune |
| 5 | Meera | Jaipur |
| 6 | Zoya | Chennai |
2Show only the name and price of every product.
हर product का केवल name और price दिखाएँ।
Hint
List the columns after SELECT, separated by commas.
Show answer and result
SELECT name, price FROM products;| name | price |
|---|---|
| Python Book | 500 |
| SQL Book | 400 |
| Keyboard | 1200 |
| Mouse | 600 |
| Notebook | 100 |
3List the cities where customers live, without repeats.
ग्राहकों के शहर बिना दोहराव के दिखाएँ।
Hint
DISTINCT removes duplicate values.
Show answer and result
SELECT DISTINCT city FROM customers;| city |
|---|
| Delhi |
| Mumbai |
| Pune |
| Jaipur |
| Chennai |
4Find all customers who live in Delhi.
दिल्ली में रहने वाले सभी ग्राहक खोजें।
Hint
Filter rows with WHERE and put text in single quotes.
Show answer and result
SELECT name, city FROM customers WHERE city = 'Delhi';| name | city |
|---|---|
| Asha | Delhi |
| Neha | Delhi |
5Show products that cost more than 500.
500 से महंगे products दिखाएँ।
Hint
Use a comparison operator in WHERE.
Show answer and result
SELECT name, price FROM products WHERE price > 500;| name | price |
|---|---|
| Keyboard | 1200 |
| Mouse | 600 |
6List products from the most expensive to the cheapest.
products को सबसे महंगे से सस्ते क्रम में दिखाएँ।
Hint
ORDER BY ... DESC sorts from high to low.
Show answer and result
SELECT name, price FROM products ORDER BY price DESC;| name | price |
|---|---|
| Keyboard | 1200 |
| Mouse | 600 |
| Python Book | 500 |
| SQL Book | 400 |
| Notebook | 100 |
7Show the two cheapest products.
सबसे सस्ते दो products दिखाएँ।
Hint
Sort ascending, then LIMIT the rows.
Show answer and result
SELECT name, price FROM products ORDER BY price ASC LIMIT 2;| name | price |
|---|---|
| Notebook | 100 |
| SQL Book | 400 |
8Show each product with its price including 18% tax, named price_with_tax.
हर product की 18% tax सहित कीमत price_with_tax नाम से दिखाएँ।
Hint
Multiply by 1.18 and rename the column with AS.
Show answer and result
SELECT name, price, ROUND(price * 1.18, 2) AS price_with_tax FROM products;| name | price | price_with_tax |
|---|---|---|
| Python Book | 500 | 590 |
| SQL Book | 400 | 472 |
| Keyboard | 1200 | 1416 |
| Mouse | 600 | 708 |
| Notebook | 100 | 118 |
9Find orders that are paid or pending.
paid या pending orders खोजें।
Hint
IN checks a column against a list of values.
Show answer and result
SELECT id, status FROM orders WHERE status IN ('paid', 'pending');| id | status |
|---|---|
| 1 | paid |
| 2 | paid |
| 3 | pending |
| 4 | paid |
| 5 | paid |
| 7 | paid |
| 8 | pending |
10Find customers whose name starts with the letter K or M.
जिन ग्राहकों का नाम K या M से शुरू होता है, उन्हें खोजें।
Hint
LIKE with % matches any following text.
Show answer and result
SELECT name FROM customers WHERE name LIKE 'K%' OR name LIKE 'M%';| name |
|---|
| Kabir |
| Meera |
Intermediate SQL practice questions
COUNT, SUM, AVG, MIN, MAX, GROUP BY, HAVING, INNER JOIN, LEFT JOIN and multi-table totals.
11How many orders are there in total?
कुल कितने orders हैं?
Hint
COUNT(*) counts rows.
12Count orders by status.
हर status के orders गिनें।
Hint
GROUP BY makes one row per status.
Show answer and result
SELECT status, COUNT(*) AS orders FROM orders GROUP BY status;| status | orders |
|---|---|
| cancelled | 1 |
| paid | 5 |
| pending | 2 |
13Show the average salary in each department, highest first.
हर department की औसत salary, सबसे ज़्यादा पहले दिखाएँ।
Hint
AVG with GROUP BY, then ORDER BY the average.
Show answer and result
SELECT department, ROUND(AVG(salary), 0) AS avg_salary FROM employees GROUP BY department ORDER BY avg_salary DESC;| department | avg_salary |
|---|---|
| Engineering | 80000 |
| Data | 70000 |
| Operations | 50000 |
14Which categories have more than one product?
किन categories में एक से ज़्यादा product हैं?
Hint
HAVING filters groups after GROUP BY.
Show answer and result
SELECT category, COUNT(*) AS products FROM products GROUP BY category HAVING COUNT(*) > 1;| category | products |
|---|---|
| Books | 2 |
| Tech | 2 |
15Show the cheapest and the most expensive price in each category.
हर category की सबसे कम और सबसे ज़्यादा कीमत दिखाएँ।
Hint
MIN and MAX work inside GROUP BY.
Show answer and result
SELECT category, MIN(price) AS lowest, MAX(price) AS highest FROM products GROUP BY category;| category | lowest | highest |
|---|---|---|
| Books | 400 | 500 |
| Stationery | 100 | 100 |
| Tech | 600 | 1200 |
16Show every order with the customer name.
हर order के साथ ग्राहक का नाम दिखाएँ।
Hint
INNER JOIN orders to customers on customer_id = customers.id.
Show answer and result
SELECT o.id AS order_id, c.name, o.status FROM orders o JOIN customers c ON c.id = o.customer_id ORDER BY o.id;| order_id | name | status |
|---|---|---|
| 1 | Asha | paid |
| 2 | Ravi | paid |
| 3 | Asha | pending |
| 4 | Neha | paid |
| 5 | Kabir | paid |
| 6 | Ravi | cancelled |
| 7 | Meera | paid |
| 8 | Neha | pending |
17List every customer and how many orders they placed, including customers with no orders.
हर ग्राहक के orders की संख्या दिखाएँ, जिनका कोई order नहीं उन्हें भी।
Hint
LEFT JOIN keeps every customer; COUNT(o.id) counts only matched orders.
Show answer and result
SELECT c.name, COUNT(o.id) AS orders FROM customers c LEFT JOIN orders o ON o.customer_id = c.id GROUP BY c.id, c.name ORDER BY orders DESC, c.name;| name | orders |
|---|---|
| Asha | 2 |
| Neha | 2 |
| Ravi | 2 |
| Kabir | 1 |
| Meera | 1 |
| Zoya | 0 |
18Find customers who have never placed an order.
जिन ग्राहकों ने कभी order नहीं किया, उन्हें खोजें।
Hint
LEFT JOIN and keep rows where the order id IS NULL.
Show answer and result
SELECT c.name FROM customers c LEFT JOIN orders o ON o.customer_id = c.id WHERE o.id IS NULL;| name |
|---|
| Zoya |
19Show total quantity sold for each product.
हर product की कुल बिकी quantity दिखाएँ।
Hint
Join order_items to products and SUM the quantity.
Show answer and result
SELECT p.name, SUM(oi.quantity) AS qty_sold FROM order_items oi JOIN products p ON p.id = oi.product_id GROUP BY p.id, p.name ORDER BY qty_sold DESC;| name | qty_sold |
|---|---|
| Notebook | 15 |
| SQL Book | 4 |
| Python Book | 3 |
| Keyboard | 3 |
| Mouse | 3 |
20Calculate the value of each order (quantity x price).
हर order की value (quantity × price) निकालें।
Hint
Join order_items with products, multiply, then SUM per order.
Show answer and result
SELECT oi.order_id, SUM(oi.quantity * p.price) AS order_value FROM order_items oi JOIN products p ON p.id = oi.product_id GROUP BY oi.order_id ORDER BY oi.order_id;| order_id | order_value |
|---|---|
| 1 | 2200 |
| 2 | 1600 |
| 3 | 500 |
| 4 | 2400 |
| 5 | 900 |
| 6 | 600 |
| 7 | 1000 |
| 8 | 800 |
Advanced SQL practice questions
Monthly totals with dates, CASE labels, subqueries, self joins, NOT EXISTS, CTEs and window functions: RANK, running totals and LAG.
21Show revenue from paid orders only, by month.
सिर्फ paid orders का revenue महीने के हिसाब से दिखाएँ।
Hint
strftime('%Y-%m', order_date) gives the month in SQLite.
Show answer and result
SELECT strftime('%Y-%m', o.order_date) AS month, SUM(oi.quantity * p.price) AS revenue FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.status = 'paid' GROUP BY month ORDER BY month;| month | revenue |
|---|---|
| 2026-01 | 6200 |
| 2026-02 | 1900 |
22Label each product as Premium (price 1000 or more), Standard (300 to 999) or Budget.
हर product को Premium, Standard या Budget लेबल दें।
Hint
CASE WHEN ... THEN ... ELSE ... END creates labels.
Show answer and result
SELECT name, price, CASE WHEN price >= 1000 THEN 'Premium' WHEN price >= 300 THEN 'Standard' ELSE 'Budget' END AS price_band FROM products;| name | price | price_band |
|---|---|---|
| Python Book | 500 | Standard |
| SQL Book | 400 | Standard |
| Keyboard | 1200 | Premium |
| Mouse | 600 | Standard |
| Notebook | 100 | Budget |
23Find employees who earn more than the company average salary.
कंपनी की औसत salary से ज़्यादा कमाने वाले employees खोजें।
Hint
Compare salary with a subquery that returns AVG(salary).
Show answer and result
SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);| name | salary |
|---|---|
| Tara | 90000 |
| Om | 70000 |
| Ira | 80000 |
24Show each employee with their manager's name.
हर employee के साथ उसके manager का नाम दिखाएँ।
Hint
Self join: join employees to employees on manager_id.
Show answer and result
SELECT e.name AS employee, m.name AS manager FROM employees e LEFT JOIN employees m ON m.id = e.manager_id ORDER BY e.id;| employee | manager |
|---|---|
| Tara | NULL |
| Om | Tara |
| Ira | Tara |
| Dev | Ira |
| Pia | Tara |
| Sam | Pia |
25Find the highest-paid employee in each department.
हर department का सबसे ज़्यादा salary वाला employee खोजें।
Hint
Use a correlated subquery or a window function.
Show answer and result
SELECT department, name, salary FROM employees e WHERE salary = (SELECT MAX(salary) FROM employees WHERE department = e.department) ORDER BY department;| department | name | salary |
|---|---|---|
| Data | Ira | 80000 |
| Engineering | Tara | 90000 |
| Operations | Pia | 50000 |
| Operations | Sam | 50000 |
26Rank employees by salary inside each department.
हर department के अंदर employees को salary से rank करें।
Hint
RANK() OVER (PARTITION BY ... ORDER BY ...).
Show answer and result
SELECT department, name, salary, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank FROM employees ORDER BY department, dept_rank;| department | name | salary | dept_rank |
|---|---|---|---|
| Data | Ira | 80000 | 1 |
| Data | Dev | 60000 | 2 |
| Engineering | Tara | 90000 | 1 |
| Engineering | Om | 70000 | 2 |
| Operations | Pia | 50000 | 1 |
| Operations | Sam | 50000 | 1 |
27Show a running total of order value by order date (paid orders).
paid orders की value का running total तारीख के क्रम में दिखाएँ।
Hint
Build order values in a CTE, then SUM(...) OVER (ORDER BY ...).
Show answer and result
WITH v AS (SELECT o.id, o.order_date, SUM(oi.quantity * p.price) AS value FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.status = 'paid' GROUP BY o.id, o.order_date) SELECT order_date, value, SUM(value) OVER (ORDER BY order_date) AS running_total FROM v ORDER BY order_date;| order_date | value | running_total |
|---|---|---|
| 2026-01-02 | 2200 | 2200 |
| 2026-01-03 | 1600 | 3800 |
| 2026-01-06 | 2400 | 6200 |
| 2026-02-01 | 900 | 7100 |
| 2026-02-05 | 1000 | 8100 |
28Find the top customer by total spend on paid orders.
paid orders पर सबसे ज़्यादा खर्च करने वाला ग्राहक खोजें।
Hint
Join four tables, SUM per customer, ORDER BY DESC, LIMIT 1.
Show answer and result
SELECT c.name, SUM(oi.quantity * p.price) AS total_spend FROM customers c JOIN orders o ON o.customer_id = c.id JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.status = 'paid' GROUP BY c.id, c.name ORDER BY total_spend DESC LIMIT 1;| name | total_spend |
|---|---|
| Neha | 2400 |
29Find customers who have never bought a Tech product.
जिन ग्राहकों ने कभी Tech product नहीं खरीदा, उन्हें खोजें।
Hint
NOT EXISTS checks that no matching row exists in a joined subquery.
Show answer and result
SELECT c.name FROM customers c WHERE NOT EXISTS (SELECT 1 FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.customer_id = c.id AND p.category = 'Tech') ORDER BY c.name;| name |
|---|
| Kabir |
| Meera |
| Zoya |
30For each order, show the customer's previous order date and the days since then.
हर order के लिए उसी ग्राहक के पिछले order से कितने दिन हुए, दिखाएँ।
Hint
LAG() returns the previous row's value; julianday() turns dates into numbers.
Show answer and result
SELECT c.name, o.order_date, LAG(o.order_date) OVER (PARTITION BY o.customer_id ORDER BY o.order_date) AS previous_order, CAST(julianday(o.order_date) - julianday(LAG(o.order_date) OVER (PARTITION BY o.customer_id ORDER BY o.order_date)) AS INTEGER) AS days_gap FROM orders o JOIN customers c ON c.id = o.customer_id ORDER BY c.name, o.order_date;| name | order_date | previous_order | days_gap |
|---|---|---|---|
| Asha | 2026-01-02 | NULL | NULL |
| Asha | 2026-01-05 | 2026-01-02 | 3 |
| Kabir | 2026-02-01 | NULL | NULL |
| Meera | 2026-02-05 | NULL | NULL |
| Neha | 2026-01-06 | NULL | NULL |
| Neha | 2026-02-08 | 2026-01-06 | 33 |
| Ravi | 2026-01-03 | NULL | NULL |
| Ravi | 2026-02-03 | 2026-01-03 | 31 |
How to use these SQL exercises
- Read the question and try the query yourself first, in the playground.
- Use the hint only if you are stuck for more than two minutes.
- Compare your result with the answer table: same rows, same order where ORDER BY is used.
- Change one thing (a filter, a column, a join type) and predict the new result before you run it.
- Finished all 30? Take the free SQL course and its final test to earn a completion certificate.
SQL practice FAQ
Where can I practise SQL online for free?
On this page. Read an exercise, write your query, then open it in the free Excel Keys SQL playground. It runs SQLite inside your browser, so there is nothing to install and no sign-up.
Which SQL is used in these exercises?
SQLite. Most queries here also work in MySQL, PostgreSQL and SQL Server. Date functions such as strftime and julianday are SQLite-specific, and the answers say so where it matters.
Are these SQL exercises good for interview preparation?
They cover the topics most beginner and analyst interviews test: filtering, sorting, GROUP BY with HAVING, joins including LEFT JOIN, subqueries, CASE, window functions such as RANK and LAG, and running totals.
Is my query or data uploaded anywhere?
No. The playground runs on your device. Your queries and any changes to the sample tables stay in your browser and reset when you reload.
Can I get a certificate?
Yes. Finish the free 32-lesson SQL course and its final test in LearnLab to download an Excel Keys completion certificate. It is a self-paced completion certificate, not an accredited qualification.
References & corrections
Full language rules are in the official SQLite documentation. Window functions are described in SQLite window functions. Found a mistake? Tell us with the exercise number.