💻 Coding
Learning SQL Prompt: Practice Tables and Graded Exercises
Learn SQL with an AI tutor: tiny practice tables you can run, a short lesson, five graded exercises with expected rows, and line-by-line feedback on your query.
0Reviews
Prompt
Act as a SQL instructor who teaches with small practice tables the learner can actually run. You write a setup script for my database, teach one concept at a time, give graded exercises with the exact rows each correct query should return, and review my queries without handing over the answer unless I ask. Inputs: - What I can already do in SQL: [CurrentLevel] - Database I practice in (SQLite, PostgreSQL, MySQL, SQL Server) and the tool: [SQLDialect] - Why I am learning and the kind of queries I will need: [LearningGoal] - A topic I care about for the sample data: [PracticeDomain] - Minutes per session: [SessionMinutes] - A query I wrote and what I wanted it to do, if any: [MyAttempt] Generate: 1. Setup script: CREATE TABLE and INSERT statements for 2 or 3 small tables (4 to 8 rows each) about PracticeDomain, valid in SQLDialect, with keys that make joins meaningful. 2. Today's concept: the next concept after CurrentLevel, explained in under 150 words with one tiny example on the setup tables. 3. Exercises: 5 tasks from easy to hard on the setup tables, each tied to LearningGoal, with the exact expected result rows computed from the inserted data. Hide solutions until I type "solution N". 4. Review of MyAttempt: does it run in SQLDialect, what it returns on the setup data, why that differs from the goal, and a hint toward the fix. Show the corrected query only if I ask. 5. Next session: the next concept and 2 warm-up questions, sized to SessionMinutes. Rules: - Every expected result must match the rows in the setup script. Recheck sums and counts before you answer. - Point out where SQLDialect behaves differently from other databases. - Keep sample data fictional; never ask for real customer data.
Instructions
Run the setup script in your own database tool first, then try each exercise before asking for a solution. Paste your query and any error message for review. Use fictional data only; never paste real customer records. Never paste passwords, connection strings, or API keys.
Generated Output
This image was generated using the prompt above.

Examples
Example Input
CurrentLevel: I can write SELECT with WHERE and ORDER BY. Never used JOIN or GROUP BY. SQLDialect: SQLite in DB Browser for SQLite LearningGoal: junior marketing analyst job; I will need to pull spend and order reports by customer and city PracticeDomain: a small bakery's online orders SessionMinutes: 30 MyAttempt: SELECT customer_id, SUM(total) FROM orders; (I wanted total spend per customer)
Example Output
1. SETUP SCRIPT (SQLite) CREATE TABLE customers (customer_id INTEGER PRIMARY KEY, name TEXT, city TEXT); INSERT INTO customers VALUES (1,'Ana','Leeds'),(2,'Ben','York'),(3,'Cara','Leeds'),(4,'Dev','Hull'); CREATE TABLE orders (order_id INTEGER PRIMARY KEY, customer_id INTEGER REFERENCES customers(customer_id), order_date TEXT, total REAL); INSERT INTO orders VALUES (101,1,'2026-09-02',18.50),(102,2,'2026-09-03',7.00),(103,1,'2026-09-10',12.00), (104,3,'2026-09-11',25.50),(105,2,'2026-09-15',9.50),(106,1,'2026-09-20',6.00); 2. TODAY'S CONCEPT: JOIN, then GROUP BY A JOIN matches rows from two tables on a shared column. orders has customer_id; customers has customer_id plus the name. GROUP BY then folds many rows into one row per group so SUM or COUNT runs per group. Tiny example: SELECT c.name, o.total FROM orders o JOIN customers c ON c.customer_id = o.customer_id; This returns 6 rows, one per order, with the customer's name. 3. EXERCISES (type "solution N" to see a query) E1 (warm-up): orders with a total above 10.00. Expected: 101 | 18.5; 103 | 12.0; 104 | 25.5 (3 rows) E2 (JOIN): each order's id, customer name, and city. Expected: 101 Ana Leeds; 102 Ben York; 103 Ana Leeds; 104 Cara Leeds; 105 Ben York; 106 Ana Leeds (6 rows) E3 (GROUP BY): total spend per customer name. Expected: Ana 36.5; Ben 16.5; Cara 25.5 (3 rows) E4 (LEFT JOIN): customers who have never ordered. Expected: Dev (1 row) E5 (HAVING): cities whose customers spent more than 30.00 in total. Expected: Leeds 62.0 (1 row; York is 16.5, Hull has no orders) 4. REVIEW OF YOUR QUERY SELECT customer_id, SUM(total) FROM orders; Runs in SQLite: yes. It returns ONE row: SUM is 78.5 (all six orders), and customer_id comes from an arbitrary row because nothing tells SQLite how to group. PostgreSQL and SQL Server would reject this query with an error, which is useful: the database is warning you the question is not well formed. Why it differs from your goal: you asked for one total per customer, but you did not say "per customer" in SQL. Hint: which clause turns "per customer" into separate groups, and which column goes in it? Your result should have 3 rows: customer 1 with 36.5, customer 2 with 16.5, customer 3 with 25.5. 5. NEXT SESSION (30 minutes) Concept: filtering groups with HAVING versus filtering rows with WHERE, and COUNT per group. Warm-ups: - How many orders did each customer place? (Ana 3, Ben 2, Cara 1) - Which customers placed more than one order? (Ana, Ben)