sql-debug-env / tasks.py
aakashdandekar's picture
Initial commit of SQL Debug Environment
be62603
Raw History Blame Contribute Delete
15.7 kB
"""
Task definitions for the SQL Debug Environment.
Each task has:
- schema_sql: CREATE TABLE statements
- sample_data: INSERT statements
- buggy_query: broken SQL
- error_message: error when running buggy_query (or "" for semantic bugs)
- task_description: what the query should do
- expected_columns: columns the correct result must have
- correct_query: reference solution (used only by grader)
- expected_rows: sorted list of tuples the correct query returns
- difficulty: easy | medium | hard
"""
from typing import Optional
TASKS = {
"easy_missing_from": {
"difficulty": "easy",
"task_description": (
"List the first name and last name of all employees in department 10, "
"ordered by last name ascending."
),
"schema_sql": """
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
department_id INTEGER NOT NULL,
salary REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO employees VALUES (1, 'Alice', 'Anderson', 10, 55000);
INSERT INTO employees VALUES (2, 'Bob', 'Brown', 20, 62000);
INSERT INTO employees VALUES (3, 'Carol', 'Clark', 10, 71000);
INSERT INTO employees VALUES (4, 'David', 'Davis', 10, 48000);
INSERT INTO employees VALUES (5, 'Eve', 'Evans', 30, 95000);
""",
"buggy_query": "SELECT first_name, last_name employees WHERE department_id = 10 ORDER BY last_name;",
"error_message": 'SyntaxError: near "employees": syntax error',
"expected_columns": ["first_name", "last_name"],
"correct_query": "SELECT first_name, last_name FROM employees WHERE department_id = 10 ORDER BY last_name;",
"expected_rows": [
("Alice", "Anderson"),
("Carol", "Clark"),
("David", "Davis"),
],
},
"easy_wrong_column": {
"difficulty": "easy",
"task_description": (
"Find the total salary budget for each department. "
"Return department_id and the total salary, ordered by department_id."
),
"schema_sql": """
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
department_id INTEGER NOT NULL,
salary REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO employees VALUES (1, 'Alice', 'Anderson', 10, 55000);
INSERT INTO employees VALUES (2, 'Bob', 'Brown', 20, 62000);
INSERT INTO employees VALUES (3, 'Carol', 'Clark', 10, 71000);
INSERT INTO employees VALUES (4, 'David', 'Davis', 10, 48000);
INSERT INTO employees VALUES (5, 'Eve', 'Evans', 30, 95000);
""",
"buggy_query": "SELECT department_id, SUM(wage) AS total_salary FROM employees GROUP BY department_id ORDER BY department_id;",
"error_message": "OperationalError: no such column: wage",
"expected_columns": ["department_id", "total_salary"],
"correct_query": "SELECT department_id, SUM(salary) AS total_salary FROM employees GROUP BY department_id ORDER BY department_id;",
"expected_rows": [(10, 174000.0), (20, 62000.0), (30, 95000.0)],
},
"easy_missing_groupby": {
"difficulty": "easy",
"task_description": (
"Count the number of orders placed by each customer. "
"Return customer_id and order_count, only for customers with more than 1 order."
),
"schema_sql": """
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
product TEXT NOT NULL,
amount REAL NOT NULL,
order_date TEXT NOT NULL
);
""",
"sample_data": """
INSERT INTO orders VALUES (1, 101, 'Laptop', 999.99, '2024-01-15');
INSERT INTO orders VALUES (2, 102, 'Mouse', 29.99, '2024-01-16');
INSERT INTO orders VALUES (3, 101, 'Keyboard', 79.99, '2024-01-17');
INSERT INTO orders VALUES (4, 103, 'Monitor', 349.99, '2024-01-18');
INSERT INTO orders VALUES (5, 101, 'Webcam', 89.99, '2024-01-19');
INSERT INTO orders VALUES (6, 102, 'Headset', 149.99, '2024-01-20');
""",
"buggy_query": "SELECT customer_id, COUNT(*) AS order_count FROM orders HAVING COUNT(*) > 1;",
"error_message": "OperationalError: HAVING clause on a non-aggregate query",
"expected_columns": ["customer_id", "order_count"],
"correct_query": "SELECT customer_id, COUNT(*) AS order_count FROM orders GROUP BY customer_id HAVING COUNT(*) > 1 ORDER BY customer_id;",
"expected_rows": [(101, 3), (102, 2)],
},
"medium_wrong_join": {
"difficulty": "medium",
"task_description": (
"List each employee's full name and their department name. "
"Return first_name, last_name, department_name ordered by last_name."
),
"schema_sql": """
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
dept_id INTEGER NOT NULL,
salary REAL NOT NULL
);
CREATE TABLE departments (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
budget REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO departments VALUES (1, 'Engineering', 500000);
INSERT INTO departments VALUES (2, 'Marketing', 200000);
INSERT INTO departments VALUES (3, 'HR', 150000);
INSERT INTO employees VALUES (1, 'Alice', 'Anderson', 1, 90000);
INSERT INTO employees VALUES (2, 'Bob', 'Brown', 2, 70000);
INSERT INTO employees VALUES (3, 'Carol', 'Clark', 1, 85000);
INSERT INTO employees VALUES (4, 'Diana', 'Dole', 3, 65000);
""",
"buggy_query": """
SELECT e.first_name, e.last_name, d.name AS department_name
FROM employees e
JOIN departments d ON e.id = d.id
ORDER BY e.last_name;
""",
"error_message": "",
"expected_columns": ["first_name", "last_name", "department_name"],
"correct_query": """
SELECT e.first_name, e.last_name, d.name AS department_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
ORDER BY e.last_name;
""",
"expected_rows": [
("Alice", "Anderson", "Engineering"),
("Bob", "Brown", "Marketing"),
("Carol", "Clark", "Engineering"),
("Diana", "Dole", "HR"),
],
},
"medium_wrong_aggregate": {
"difficulty": "medium",
"task_description": (
"Find the average order amount per customer. "
"Return customer_id and avg_amount for customers whose average is above 100. "
"Order by avg_amount descending."
),
"schema_sql": """
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
product TEXT NOT NULL,
amount REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO orders VALUES (1, 101, 'Laptop', 999.99);
INSERT INTO orders VALUES (2, 102, 'Mouse', 29.99);
INSERT INTO orders VALUES (3, 101, 'Keyboard', 79.99);
INSERT INTO orders VALUES (4, 103, 'Monitor', 349.99);
INSERT INTO orders VALUES (5, 101, 'Webcam', 89.99);
INSERT INTO orders VALUES (6, 102, 'Headset', 149.99);
INSERT INTO orders VALUES (7, 103, 'Desk', 599.99);
""",
"buggy_query": """
SELECT customer_id, SUM(amount) AS avg_amount
FROM orders
GROUP BY customer_id
HAVING avg_amount > 100
ORDER BY avg_amount DESC;
""",
"error_message": "",
"expected_columns": ["customer_id", "avg_amount"],
"correct_query": """
SELECT customer_id, AVG(amount) AS avg_amount
FROM orders
GROUP BY customer_id
HAVING AVG(amount) > 100
ORDER BY avg_amount DESC;
""",
"expected_rows": [(103, 474.99), (101, 389.99)],
},
"medium_inner_vs_left": {
"difficulty": "medium",
"task_description": (
"List ALL customers along with the total amount they've spent. "
"Customers who haven't placed any orders should show 0.0 as total_spent. "
"Return customer_id, customer_name, total_spent ordered by customer_id."
),
"schema_sql": """
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
amount REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO customers VALUES (1, 'Alice', 'alice@example.com');
INSERT INTO customers VALUES (2, 'Bob', 'bob@example.com');
INSERT INTO customers VALUES (3, 'Carol', 'carol@example.com');
INSERT INTO customers VALUES (4, 'Dave', 'dave@example.com');
INSERT INTO orders VALUES (1, 1, 250.00);
INSERT INTO orders VALUES (2, 1, 150.00);
INSERT INTO orders VALUES (3, 3, 400.00);
""",
"buggy_query": """
SELECT c.id AS customer_id, c.name AS customer_name, SUM(o.amount) AS total_spent
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;
""",
"error_message": "",
"expected_columns": ["customer_id", "customer_name", "total_spent"],
"correct_query": """
SELECT c.id AS customer_id, c.name AS customer_name, COALESCE(SUM(o.amount), 0.0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;
""",
"expected_rows": [
(1, "Alice", 400.0),
(2, "Bob", 0.0),
(3, "Carol", 400.0),
(4, "Dave", 0.0),
],
},
"hard_window_function": {
"difficulty": "hard",
"task_description": (
"For each employee, compute a running_avg_salary: the average of the current "
"employee's salary and the employee immediately before them in the same department "
"(ordered by id). For the first employee in each department, it equals their own salary. "
"Return dept_id, first_name, id, salary, running_avg_salary ordered by dept_id, id."
),
"schema_sql": """
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
dept_id INTEGER NOT NULL,
salary REAL NOT NULL
);
""",
"sample_data": """
INSERT INTO employees VALUES (1, 'Alice', 1, 80000);
INSERT INTO employees VALUES (2, 'Bob', 1, 90000);
INSERT INTO employees VALUES (3, 'Carol', 2, 70000);
INSERT INTO employees VALUES (4, 'Diana', 2, 60000);
INSERT INTO employees VALUES (5, 'Eve', 2, 80000);
""",
"buggy_query": """
WITH lagged AS (
SELECT id, first_name, dept_id, salary,
LAG(salary, 2) OVER (PARTITION BY dept_id ORDER BY id) AS prev_salary
FROM employees
)
SELECT dept_id, first_name, id, salary,
ROUND((salary + COALESCE(prev_salary, salary)) / 2.0, 2) AS running_avg_salary
FROM lagged
ORDER BY dept_id, id;
""",
"error_message": "",
"expected_columns": [
"dept_id",
"first_name",
"id",
"salary",
"running_avg_salary",
],
"correct_query": """
WITH lagged AS (
SELECT id, first_name, dept_id, salary,
LAG(salary, 1) OVER (PARTITION BY dept_id ORDER BY id) AS prev_salary
FROM employees
)
SELECT dept_id, first_name, id, salary,
ROUND((salary + COALESCE(prev_salary, salary)) / 2.0, 2) AS running_avg_salary
FROM lagged
ORDER BY dept_id, id;
""",
"expected_rows": [
(1, "Alice", 1, 80000.0, 80000.0),
(1, "Bob", 2, 90000.0, 85000.0),
(2, "Carol", 3, 70000.0, 70000.0),
(2, "Diana", 4, 60000.0, 65000.0),
(2, "Eve", 5, 80000.0, 70000.0),
],
},
"hard_correlated_subquery": {
"difficulty": "hard",
"task_description": (
"Find all products that have never been ordered. "
"Return product_id and product_name ordered by product_id."
),
"schema_sql": """
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL,
category TEXT NOT NULL
);
CREATE TABLE order_items (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL
);
""",
"sample_data": """
INSERT INTO products VALUES (1, 'Laptop', 999.99, 'Electronics');
INSERT INTO products VALUES (2, 'Mouse', 29.99, 'Electronics');
INSERT INTO products VALUES (3, 'Desk', 299.99, 'Furniture');
INSERT INTO products VALUES (4, 'Chair', 199.99, 'Furniture');
INSERT INTO products VALUES (5, 'Keyboard', 79.99, 'Electronics');
INSERT INTO order_items VALUES (1, 101, 1, 2);
INSERT INTO order_items VALUES (2, 101, 3, 1);
INSERT INTO order_items VALUES (3, 102, 2, 4);
""",
"buggy_query": """
SELECT p.id AS product_id, p.name AS product_name
FROM products p
WHERE EXISTS (
SELECT 1 FROM order_items oi WHERE oi.product_id = p.id
)
ORDER BY p.id;
""",
"error_message": "",
"expected_columns": ["product_id", "product_name"],
"correct_query": """
SELECT p.id AS product_id, p.name AS product_name
FROM products p
WHERE NOT EXISTS (
SELECT 1 FROM order_items oi WHERE oi.product_id = p.id
)
ORDER BY p.id;
""",
"expected_rows": [(4, "Chair"), (5, "Keyboard")],
},
"hard_multi_cte": {
"difficulty": "hard",
"task_description": (
"Calculate month-over-month revenue change. "
"For each month (in YYYY-MM format), return the month, total_revenue, "
"and revenue_change (current month minus previous month). "
"For the first month, revenue_change should be 0. "
"Order by month ascending."
),
"schema_sql": """
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
sale_date TEXT NOT NULL,
amount REAL NOT NULL,
region TEXT NOT NULL
);
""",
"sample_data": """
INSERT INTO sales VALUES (1, '2024-01-05', 10000, 'North');
INSERT INTO sales VALUES (2, '2024-01-20', 15000, 'South');
INSERT INTO sales VALUES (3, '2024-02-08', 20000, 'North');
INSERT INTO sales VALUES (4, '2024-02-14', 12000, 'West');
INSERT INTO sales VALUES (5, '2024-03-01', 18000, 'South');
INSERT INTO sales VALUES (6, '2024-03-22', 22000, 'North');
""",
"buggy_query": """
WITH monthly AS (
SELECT strftime('%Y-%m', sale_date) AS month,
SUM(amount) AS total_revenue
FROM sales
GROUP BY month
),
with_prev AS (
SELECT month, total_revenue,
LAG(total_revenue, 2) OVER (ORDER BY month) AS prev_revenue
FROM monthly
)
SELECT month, total_revenue,
COALESCE(total_revenue - prev_revenue, 0) AS revenue_change
FROM with_prev
ORDER BY month;
""",
"error_message": "",
"expected_columns": ["month", "total_revenue", "revenue_change"],
"correct_query": """
WITH monthly AS (
SELECT strftime('%Y-%m', sale_date) AS month,
SUM(amount) AS total_revenue
FROM sales
GROUP BY month
),
with_prev AS (
SELECT month, total_revenue,
LAG(total_revenue, 1) OVER (ORDER BY month) AS prev_revenue
FROM monthly
)
SELECT month, total_revenue,
COALESCE(total_revenue - prev_revenue, 0) AS revenue_change
FROM with_prev
ORDER BY month;
""",
"expected_rows": [
("2024-01", 25000.0, 0.0),
("2024-02", 32000.0, 7000.0),
("2024-03", 40000.0, 8000.0),
],
},
}
def get_tasks_by_difficulty(difficulty: str) -> dict:
return {k: v for k, v in TASKS.items() if v["difficulty"] == difficulty}
def get_task(task_id: str) -> Optional[dict]:
return TASKS.get(task_id)
TASK_GROUPS = {
"easy_syntax_fix": [
"easy_missing_from",
"easy_wrong_column",
"easy_missing_groupby",
],
"medium_logic_fix": [
"medium_wrong_join",
"medium_wrong_aggregate",
"medium_inner_vs_left",
],
"hard_optimization_fix": [
"hard_window_function",
"hard_correlated_subquery",
"hard_multi_cte",
],
}