Hands-On SQL Practice with the Query Emulator

Practice Makes Perfect

You've learned SQL syntax, explored aggregate functions, mastered joins, and understood transactions. Now it's time to practice. Reading about SQL and writing SQL are two very different skills – the only way to become proficient is through hands-on experience.

This lesson provides an interactive SQL query emulator that runs entirely in your browser. It uses the same e-commerce schema you've seen throughout the SQL lessons: Users, Categories, Products, Orders, and OrderItems. The database is pre-populated with realistic test data.

You can write queries, execute them instantly, and see results – all without installing a database server or setting up any infrastructure. The emulator supports the core SQL features you've learned, and its logic has been thoroughly tested against the challenge queries below to ensure accuracy and reliability:

  • SELECT with specific columns or *
  • WHERE clauses with comparison operators (=, >, <, >=, <=, !=)
  • AND, OR, NOT logical operators
  • IN, BETWEEN, LIKE operators
  • ORDER BY with ASC/DESC
  • LIMIT for result pagination
  • Aggregate functions: COUNT(), SUM(), AVG(), MIN(), MAX()
  • GROUP BY with HAVING
  • INNER JOIN and LEFT JOIN
  • UNION and UNION ALL
  • CASE expressions
  • COALESCE and NULLIF
  • EXISTS and NOT EXISTS

⚠️ Please note: while the emulator has been validated on these queries, more advanced or untested SQL patterns may not behave exactly as they would in a production database.

The challenges below start simple and gradually increase in complexity, helping you build confidence and proficiency.

Database Schema

Here's the e-commerce database structure you'll be working with. Understanding these relationships is crucial for writing effective queries, especially when using JOIN operations.

erDiagram Users ||--o{ Orders : places Categories ||--o{ Products : contains Products ||--o{ OrderItems : "ordered in" Orders ||--o{ OrderItems : includes Users { int UserId PK string Email string FirstName string LastName date CreatedDate bool IsActive date DeactivatedDate } Categories { int CategoryId PK string CategoryName } Products { int ProductId PK string Name string Description decimal Price int CategoryId FK int StockQuantity bool IsActive } Orders { int OrderId PK int UserId FK date OrderDate decimal TotalAmount string Status date ShipmentDate date CompletedDate } OrderItems { int OrderItemId PK int OrderId FK int ProductId FK int Quantity decimal UnitPrice }

Reading the Schema

  • PK (Primary Key): Uniquely identifies each record in the table
  • FK (Foreign Key): References a primary key in another table, creating relationships
  • Lines between tables: Show how data connects – for example, each Order belongs to one User

The Query Emulator

Below is the interactive SQL emulator. Type your queries in the text area and click "Run Query" to see results. The emulator shows you the resulting data table, the number of rows returned, and any error messages if your query has syntax issues.

Emulator Tips

  • Start simple: Begin with basic SELECT * FROM Users to see the data structure
  • Case-insensitive: SQL keywords work in any case (SELECT or select)
  • Check your results: The emulator displays row counts and execution feedback
  • Experiment freely: This is a read-only emulator – you can't break anything!

SQL Query Emulator

Practice Challenges

Work through these challenges to build your SQL skills. Start with Level 1 and progress to more complex queries. Try solving each challenge before looking at the solution.

Level 1: Basic Queries

Challenge 1.1: Select all columns from the Users table.

Show Solution
SELECT * FROM Users;

Challenge 1.2: Select only the ProductId, Name, and Price columns from the Products table.

Show Solution
SELECT ProductId, Name, Price FROM Products;

Challenge 1.3: Find all active users (where IsActive = 1).

Show Solution
SELECT * FROM Users WHERE IsActive = 1;

Challenge 1.4: Find all products with a price greater than $50.

Show Solution
SELECT * FROM Products WHERE Price > 50;

Challenge 1.5: List all products, ordered by price from highest to lowest.

Show Solution
SELECT * FROM Products ORDER BY Price DESC;

Level 2: Filtering and Sorting

Challenge 2.1: Find all orders with status 'Completed' that have a total amount greater than $100.

Show Solution
SELECT * FROM Orders
WHERE Status = 'Completed' AND TotalAmount > 100;

Challenge 2.2: Find products with prices between $20 and $100.

Show Solution
SELECT * FROM Products
WHERE Price BETWEEN 20 AND 100;

Challenge 2.3: Find all users whose email contains 'gmail.com'.

Show Solution
SELECT * FROM Users
WHERE Email LIKE '%gmail.com%';

Challenge 2.4: Show the 5 most expensive products.

Show Solution
SELECT * FROM Products
ORDER BY Price DESC
LIMIT 5;

Challenge 2.5: Find orders with status of either 'Pending' or 'Processing'.

Show Solution
SELECT * FROM Orders
WHERE Status IN ('Pending', 'Processing');

Level 3: Aggregate Functions

Challenge 3.1: Count the total number of users in the database.

Show Solution
SELECT COUNT(*) AS TotalUsers FROM Users;

Challenge 3.2: Calculate the average price of all products.

Show Solution
SELECT AVG(Price) AS AveragePrice FROM Products;

Challenge 3.3: Find the total revenue from all completed orders.

Show Solution
SELECT SUM(TotalAmount) AS TotalRevenue
FROM Orders
WHERE Status = 'Completed';

Challenge 3.4: Find the minimum and maximum product prices.

Show Solution
SELECT MIN(Price) AS MinPrice, MAX(Price) AS MaxPrice
FROM Products;

Level 4: GROUP BY and Aggregation

Challenge 4.1: Count how many orders each user has placed.

Show Solution
SELECT UserId, COUNT(*) AS OrderCount
FROM Orders
GROUP BY UserId;

Challenge 4.2: Calculate average price and total stock for each category.

Show Solution
SELECT CategoryId,
       AVG(Price) AS AvgPrice,
       SUM(StockQuantity) AS TotalStock
FROM Products
GROUP BY CategoryId;

Challenge 4.3: Find users who have placed more than 2 orders.

Show Solution
SELECT UserId, COUNT(*) AS OrderCount
FROM Orders
GROUP BY UserId
HAVING COUNT(*) > 2;

Challenge 4.4: Show order counts by status, ordered from most to least common.

Show Solution
SELECT Status, COUNT(*) AS Count
FROM Orders
GROUP BY Status
ORDER BY Count DESC;

Level 5: JOINs

Challenge 5.1: Show all orders with the user's first and last name.

Show Solution
SELECT o.OrderId, o.OrderDate, o.TotalAmount,
       u.FirstName, u.LastName
FROM Orders o
INNER JOIN Users u ON o.UserId = u.UserId;

Challenge 5.2: Show all products with their category names.

Show Solution
SELECT p.ProductId, p.Name, p.Price, c.CategoryName
FROM Products p
INNER JOIN Categories c ON p.CategoryId = c.CategoryId;

Challenge 5.3: List all users with their order count (including users with zero orders).

Show Solution
SELECT u.UserId, u.Email, COUNT(o.OrderId) AS OrderCount
FROM Users u
LEFT JOIN Orders o ON u.UserId = o.UserId
GROUP BY u.UserId, u.Email;

Challenge 5.4: Show order details with product names (join Orders → OrderItems → Products).

Show Solution
SELECT o.OrderId, oi.Quantity, p.Name, oi.UnitPrice
FROM Orders o
INNER JOIN OrderItems oi ON o.OrderId = oi.OrderId
INNER JOIN Products p ON oi.ProductId = p.ProductId;

Level 6: Advanced Queries

Challenge 6.1: Find products more expensive than the average product price.

Show Solution
SELECT * FROM Products
WHERE Price > (SELECT AVG(Price) FROM Products);

Challenge 6.2: Categorize products by price range using CASE.

Show Solution
SELECT Name, Price,
       CASE
           WHEN Price < 30 THEN 'Budget'
           WHEN Price BETWEEN 30 AND 100 THEN 'Mid-Range'
           ELSE 'Premium'
       END AS PriceCategory
FROM Products;

Challenge 6.3: Combine active and inactive users into one result with a status label.

Show Solution
SELECT UserId, Email, 'Active' AS Status
FROM Users WHERE IsActive = 1
UNION ALL
SELECT UserId, Email, 'Inactive' AS Status
FROM Users WHERE IsActive = 0;

Challenge 6.4: Calculate revenue per user for users with completed orders.

Show Solution
SELECT u.UserId, u.Email,
       COALESCE(SUM(o.TotalAmount), 0) AS TotalRevenue
FROM Users u
LEFT JOIN Orders o ON u.UserId = o.UserId AND o.Status = 'Completed'
GROUP BY u.UserId, u.Email
ORDER BY TotalRevenue DESC;

Keep experimenting with different query combinations. The more you practice, the more natural SQL will become!