Krishna Prakash
commited on
Commit
·
e7cf806
1
Parent(s):
55cf619
Initial commit For SQL Practice Platform
Browse files- Dockerfile +8 -0
- app/__init__.py +0 -0
- app/__pycache__/__init__.cpython-313.pyc +0 -0
- app/__pycache__/database.cpython-313.pyc +0 -0
- app/__pycache__/main.cpython-313.pyc +0 -0
- app/__pycache__/schemas.cpython-313.pyc +0 -0
- app/database.py +13 -0
- app/main.py +178 -0
- app/questions/banking.json +242 -0
- app/questions/college.json +242 -0
- app/questions/e_commerce.json +242 -0
- app/questions/hospital.json +242 -0
- app/questions/inventory.json +242 -0
- app/questions/library.json +242 -0
- app/questions/restaurant.json +242 -0
- app/questions/retail.json +242 -0
- app/schemas.py +8 -0
- app/schemas/banking.sql +34 -0
- app/schemas/college.sql +52 -0
- app/schemas/e_commerce.sql +44 -0
- app/schemas/hospital.sql +50 -0
- app/schemas/inventory.sql +46 -0
- app/schemas/library.sql +55 -0
- app/schemas/restaurant.sql +65 -0
- app/schemas/retail.sql +42 -0
- app/static/ace.js +0 -0
- app/static/favicon.png +0 -0
- app/static/index.html +128 -0
- app/static/script.js +603 -0
- app/static/style.css +16 -0
- requirements.txt +4 -0
Dockerfile
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
COPY requirements.txt .
|
5 |
+
RUN pip install -r requirements.txt
|
6 |
+
COPY app/ .
|
7 |
+
|
8 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
app/__init__.py
ADDED
File without changes
|
app/__pycache__/__init__.cpython-313.pyc
ADDED
Binary file (137 Bytes). View file
|
|
app/__pycache__/database.cpython-313.pyc
ADDED
Binary file (891 Bytes). View file
|
|
app/__pycache__/main.cpython-313.pyc
ADDED
Binary file (19 kB). View file
|
|
app/__pycache__/schemas.cpython-313.pyc
ADDED
Binary file (694 Bytes). View file
|
|
app/database.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
|
3 |
+
def create_session_db():
|
4 |
+
conn = sqlite3.connect(":memory:", check_same_thread=False)
|
5 |
+
conn.row_factory = sqlite3.Row
|
6 |
+
return conn
|
7 |
+
|
8 |
+
def get_session_db(session_id: str):
|
9 |
+
from main import sessions
|
10 |
+
return sessions.get(session_id, {}).get("conn")
|
11 |
+
|
12 |
+
def close_session_db(conn):
|
13 |
+
conn.close()
|
app/main.py
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, HTTPException, Header, Request
|
2 |
+
from fastapi.responses import JSONResponse, HTMLResponse
|
3 |
+
from fastapi.staticfiles import StaticFiles
|
4 |
+
from pydantic import BaseModel
|
5 |
+
import sqlite3
|
6 |
+
import sqlparse
|
7 |
+
import os
|
8 |
+
import uuid
|
9 |
+
import json # Added for json.load()
|
10 |
+
from typing import Dict, Any
|
11 |
+
from app.database import create_session_db, close_session_db
|
12 |
+
from app.schemas import RunQueryRequest, ValidateQueryRequest
|
13 |
+
|
14 |
+
app = FastAPI()
|
15 |
+
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static")
|
16 |
+
|
17 |
+
@app.exception_handler(Exception)
|
18 |
+
async def custom_exception_handler(request: Request, exc: Exception):
|
19 |
+
return JSONResponse(status_code=500, content={"detail": str(exc)})
|
20 |
+
|
21 |
+
sessions: Dict[str, dict] = {}
|
22 |
+
|
23 |
+
BASE_DIR = os.path.dirname(__file__)
|
24 |
+
|
25 |
+
def load_questions(domain: str):
|
26 |
+
file_path = os.path.join(BASE_DIR, "questions", f"{domain}.json")
|
27 |
+
if not os.path.exists(file_path):
|
28 |
+
raise FileNotFoundError(f"Question file not found: {file_path}")
|
29 |
+
with open(file_path, "r") as f:
|
30 |
+
return json.load(f) # Replaced eval with json.load()
|
31 |
+
|
32 |
+
def load_schema_sql(domain: str):
|
33 |
+
file_path = os.path.join(BASE_DIR, "schemas", f"{domain}.sql")
|
34 |
+
if not os.path.exists(file_path):
|
35 |
+
raise FileNotFoundError(f"Schema file not found: {file_path}")
|
36 |
+
with open(file_path, "r") as f:
|
37 |
+
return f.read()
|
38 |
+
|
39 |
+
def is_safe_query(sql: str) -> bool:
|
40 |
+
parsed = sqlparse.parse(sql.lower())[0]
|
41 |
+
return str(parsed).lower().strip().startswith("select") and all(kw not in str(parsed).lower() for kw in ["drop", "attach", "detach", "pragma", "insert", "update", "delete"])
|
42 |
+
|
43 |
+
def extract_tables(sql: str) -> list:
|
44 |
+
tables = set()
|
45 |
+
tokens = sql.replace("\n", " ").lower().split()
|
46 |
+
in_subquery = in_openquery = in_values = False
|
47 |
+
|
48 |
+
for i, token in enumerate(tokens):
|
49 |
+
if token == "(" and not in_subquery and not in_values:
|
50 |
+
in_values = i > 0 and tokens[i - 1] == "values"
|
51 |
+
in_subquery = not in_values
|
52 |
+
if token == ")" and (in_subquery or in_values):
|
53 |
+
if in_values and i + 1 < len(tokens) and tokens[i + 1] == "as": in_values = False
|
54 |
+
elif in_subquery: in_subquery = False
|
55 |
+
if token == "openquery" and i + 1 < len(tokens) and tokens[i + 1] == "(": in_openquery = True
|
56 |
+
if token == ")" and in_openquery: in_openquery = False
|
57 |
+
if in_openquery: continue
|
58 |
+
|
59 |
+
if token in ["from", "join", "update", "delete", "insert", "into", "using", "apply", "pivot", "table"]:
|
60 |
+
next_token = tokens[i + 1].replace(",);", "") if i + 1 < len(tokens) else ""
|
61 |
+
if next_token and next_token not in ["select", "where", "on", "order", "group", "having", "as", "("]:
|
62 |
+
if i + 2 < len(tokens) and tokens[i + 2] == "as": next_token = next_token
|
63 |
+
elif next_token not in ["left", "right", "inner", "outer", "cross", "full"]: tables.add(next_token)
|
64 |
+
i += 1
|
65 |
+
elif token == "merge" and i + 1 < len(tokens) and tokens[i + 1] == "into":
|
66 |
+
next_token = tokens[i + 2].replace(",);", "") if i + 2 < len(tokens) else ""
|
67 |
+
if next_token and next_token not in ["using", "select", "where"]: tables.add(next_token)
|
68 |
+
i += 2
|
69 |
+
while i + 1 < len(tokens) and tokens[i + 1] != "using": i += 1
|
70 |
+
if i + 2 < len(tokens) and (next_token := tokens[i + 2].replace(",);", "")) and next_token not in ["select", "where"]: tables.add(next_token)
|
71 |
+
elif token == "select" and i + 1 < len(tokens) and tokens[i + 1] == "into":
|
72 |
+
next_token = tokens[i + 2].replace(",);", "") if i + 2 < len(tokens) else ""
|
73 |
+
if next_token and next_token not in ["from", "select"]: tables.add(next_token)
|
74 |
+
i += 2
|
75 |
+
while i + 1 < len(tokens) and tokens[i + 1] != "from": i += 1
|
76 |
+
if i + 2 < len(tokens) and (next_token := tokens[i + 2].replace(",);", "")) and next_token not in ["where", "join"]: tables.add(next_token)
|
77 |
+
elif token == "with":
|
78 |
+
while i + 1 < len(tokens) and tokens[i + 1] != "as": i += 1
|
79 |
+
if i + 2 < len(tokens) and tokens[i + 2] == "(":
|
80 |
+
bracket_count = 1
|
81 |
+
subquery_start = i + 2
|
82 |
+
i += 2
|
83 |
+
while i < len(tokens) and bracket_count > 0:
|
84 |
+
if tokens[i] == "(": bracket_count += 1
|
85 |
+
elif tokens[i] == ")": bracket_count -= 1
|
86 |
+
i += 1
|
87 |
+
if bracket_count == 0 and i > subquery_start:
|
88 |
+
subquery = " ".join(tokens[subquery_start:i - 1])
|
89 |
+
tables.update(t for t in extract_tables(subquery) if t not in tables)
|
90 |
+
elif token == "values" and i + 1 < len(tokens) and tokens[i + 1] == "(":
|
91 |
+
while i + 1 < len(tokens) and tokens[i + 1] != "as": i += 1
|
92 |
+
if i + 2 < len(tokens) and (alias := tokens[i + 2].replace(",);", "")): tables.add(alias)
|
93 |
+
elif token in ["exists", "in"]:
|
94 |
+
subquery_start = i + 1
|
95 |
+
while i + 1 < len(tokens) and tokens[i + 1] != ")": i += 1
|
96 |
+
if i > subquery_start:
|
97 |
+
subquery = " ".join(tokens[subquery_start:i + 1])
|
98 |
+
tables.update(t for t in extract_tables(subquery) if t not in tables)
|
99 |
+
return list(tables)
|
100 |
+
|
101 |
+
@app.post("/api/session")
|
102 |
+
async def create_session():
|
103 |
+
session_id = str(uuid.uuid4())
|
104 |
+
sessions[session_id] = {"conn": create_session_db(), "domain": None}
|
105 |
+
return {"session_id": session_id}
|
106 |
+
|
107 |
+
@app.get("/api/databases")
|
108 |
+
async def get_databases():
|
109 |
+
questions_dir = os.path.join(BASE_DIR, "questions")
|
110 |
+
return {"databases": [f.replace(".json", "") for f in os.listdir(questions_dir) if f.endswith(".json")] if os.path.exists(questions_dir) else []}
|
111 |
+
|
112 |
+
@app.post("/api/load-schema/{domain}")
|
113 |
+
async def load_schema(domain: str, session_id: str = Header(...)):
|
114 |
+
if session_id not in sessions: raise HTTPException(status_code=401, detail="Invalid session")
|
115 |
+
sessions[session_id] = {"conn": create_session_db(), "domain": domain}
|
116 |
+
try:
|
117 |
+
sessions[session_id]["conn"].executescript(load_schema_sql(domain))
|
118 |
+
sessions[session_id]["conn"].commit()
|
119 |
+
except sqlite3.Error as e:
|
120 |
+
close_session_db(sessions[session_id]["conn"]) # Cleanup on error
|
121 |
+
del sessions[session_id]
|
122 |
+
raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
|
123 |
+
return {"message": f"Database {domain} loaded"}
|
124 |
+
|
125 |
+
@app.get("/api/schema/{domain}")
|
126 |
+
async def get_schema(domain: str, session_id: str = Header(...)):
|
127 |
+
if session_id not in sessions or sessions[session_id]["domain"] != domain: raise HTTPException(status_code=401, detail="Invalid session or domain not loaded")
|
128 |
+
conn = sessions[session_id]["conn"]
|
129 |
+
cursor = conn.cursor()
|
130 |
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
131 |
+
return {"schema": {table: [{"name": row["name"], "type": row["type"]} for row in conn.execute(f"PRAGMA table_info({table});")] for table in [row["name"] for row in cursor.fetchall()]}}
|
132 |
+
|
133 |
+
@app.get("/api/sample-data/{domain}")
|
134 |
+
async def get_sample_data(domain: str, session_id: str = Header(...)):
|
135 |
+
if session_id not in sessions or sessions[session_id]["domain"] != domain: raise HTTPException(status_code=401, detail="Invalid session or domain not loaded")
|
136 |
+
conn = sessions[session_id]["conn"]
|
137 |
+
cursor = conn.cursor()
|
138 |
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
139 |
+
return {"sample_data": {table: {"columns": [desc[0] for desc in conn.execute(f"SELECT * FROM {table} LIMIT 5").description], "rows": [dict(row) for row in conn.execute(f"SELECT * FROM {table} LIMIT 5")]} for table in [row["name"] for row in cursor.fetchall()]}}
|
140 |
+
|
141 |
+
@app.post("/api/run-query")
|
142 |
+
async def run_query(request: RunQueryRequest, session_id: str = Header(...)):
|
143 |
+
if session_id not in sessions or not sessions[session_id]["domain"]: raise HTTPException(status_code=401, detail="Invalid session or no database loaded")
|
144 |
+
if not is_safe_query(request.query): raise HTTPException(status_code=400, detail="Only SELECT queries are allowed")
|
145 |
+
conn = sessions[session_id]["conn"]
|
146 |
+
cursor = conn.cursor()
|
147 |
+
cursor.execute(request.query)
|
148 |
+
if cursor.description:
|
149 |
+
columns = [desc[0] for desc in cursor.description]
|
150 |
+
return {"columns": columns, "rows": [dict(zip(columns, row)) for row in cursor.fetchall()]}
|
151 |
+
return {"message": "Query executed successfully (no results)"}
|
152 |
+
|
153 |
+
@app.get("/api/questions/{domain}")
|
154 |
+
async def get_questions(domain: str, difficulty: str = None):
|
155 |
+
questions = load_questions(domain)
|
156 |
+
if difficulty: questions = [q for q in questions if q["difficulty"].lower() == difficulty.lower()]
|
157 |
+
return [{"id": q["id"], "title": q["title"], "difficulty": q["difficulty"], "description": q["description"], "hint": q["hint"], "expected_sql": q["expected_sql"]} for q in questions]
|
158 |
+
|
159 |
+
@app.post("/api/validate")
|
160 |
+
async def validate_query(request: ValidateQueryRequest, session_id: str = Header(...)):
|
161 |
+
if session_id not in sessions or not sessions[session_id]["domain"]: raise HTTPException(status_code=401, detail="Invalid session or no database loaded")
|
162 |
+
conn = sessions[session_id]["conn"]
|
163 |
+
cursor = conn.cursor()
|
164 |
+
cursor.execute(request.user_query)
|
165 |
+
user_result = [tuple(str(x).lower() for x in row) for row in cursor.fetchall()] if cursor.description else []
|
166 |
+
cursor.execute(request.expected_query)
|
167 |
+
expected_result = [tuple(str(x).lower() for x in row) for row in cursor.fetchall()] if cursor.description else []
|
168 |
+
return {"valid": user_result == expected_result, "error": "Results do not match." if user_result != expected_result else ""}
|
169 |
+
|
170 |
+
@app.on_event("shutdown")
|
171 |
+
async def cleanup():
|
172 |
+
for session_id in list(sessions): close_session_db(sessions[session_id]["conn"]); del sessions[session_id]
|
173 |
+
|
174 |
+
@app.get("/", response_class=HTMLResponse)
|
175 |
+
async def serve_frontend():
|
176 |
+
file_path = os.path.join(BASE_DIR, "static", "index.html")
|
177 |
+
if not os.path.exists(file_path): raise HTTPException(status_code=500, detail=f"Frontend file not found: {file_path}")
|
178 |
+
with open(file_path, "r") as f: return f.read()
|
app/questions/banking.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List all customers",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and emails of all customers.",
|
7 |
+
"hint": "Use SELECT on the customers table.",
|
8 |
+
"expected_sql": "SELECT name, email FROM customers;"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Show savings accounts",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "List all accounts of type 'Savings' with their balances.",
|
15 |
+
"hint": "Use WHERE clause to filter by account_type.",
|
16 |
+
"expected_sql": "SELECT account_id, balance FROM accounts WHERE account_type = 'Savings';"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "Find customers in New York",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Show the names and ages of customers living in New York.",
|
23 |
+
"hint": "Use WHERE clause to filter by city.",
|
24 |
+
"expected_sql": "SELECT name, age FROM customers WHERE city = 'New York';"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Count total accounts",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Count the total number of accounts in the system.",
|
31 |
+
"hint": "Use COUNT() function on the accounts table.",
|
32 |
+
"expected_sql": "SELECT COUNT(*) AS total_accounts FROM accounts;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List accounts opened in 2023",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all accounts opened in 2023.",
|
39 |
+
"hint": "Use WHERE clause with LIKE on opened_on.",
|
40 |
+
"expected_sql": "SELECT account_id, account_type, opened_on FROM accounts WHERE opened_on LIKE '2023%';"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "List distinct account types",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Get all unique account types from the accounts table.",
|
47 |
+
"hint": "Use DISTINCT to avoid duplicate account types.",
|
48 |
+
"expected_sql": "SELECT DISTINCT account_type FROM accounts;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "Find accounts for John Doe",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "List all accounts owned by John Doe.",
|
55 |
+
"hint": "Join accounts with customers and filter by name.",
|
56 |
+
"expected_sql": "SELECT a.account_id, a.account_type, a.balance FROM accounts a JOIN customers c ON a.customer_id = c.customer_id WHERE c.name = 'John Doe';"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Show customers under 30",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve the names and cities of customers younger than 30 years old.",
|
63 |
+
"hint": "Use WHERE clause to filter by age.",
|
64 |
+
"expected_sql": "SELECT name, city FROM customers WHERE age < 30;"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List loan accounts",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show all accounts of type 'Loan' with their balances.",
|
71 |
+
"hint": "Use WHERE clause to filter by account_type.",
|
72 |
+
"expected_sql": "SELECT account_id, balance FROM accounts WHERE account_type = 'Loan';"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "Find customer emails",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Retrieve the names and email addresses of all customers.",
|
79 |
+
"hint": "Select name and email from customers table.",
|
80 |
+
"expected_sql": "SELECT name, email FROM customers;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Count accounts per customer",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Show the number of accounts each customer owns.",
|
87 |
+
"hint": "Join customers and accounts, then GROUP BY customer name.",
|
88 |
+
"expected_sql": "SELECT c.name, COUNT(a.account_id) AS account_count FROM customers c LEFT JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Total balance per customer",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Calculate the total balance across all accounts for each customer.",
|
95 |
+
"hint": "Join customers and accounts, then GROUP BY customer name and SUM balance.",
|
96 |
+
"expected_sql": "SELECT c.name, SUM(a.balance) AS total_balance FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Average balance by account type",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Find the average balance for each account type.",
|
103 |
+
"hint": "GROUP BY account_type and use AVG on balance.",
|
104 |
+
"expected_sql": "SELECT account_type, AVG(balance) AS avg_balance FROM accounts GROUP BY account_type;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Customers with multiple accounts",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "List customers who have more than one account.",
|
111 |
+
"hint": "Join customers and accounts, GROUP BY customer, and use HAVING clause.",
|
112 |
+
"expected_sql": "SELECT c.name, COUNT(a.account_id) AS account_count FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name HAVING COUNT(a.account_id) > 1;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Accounts with negative balance",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "Show all accounts with a negative balance, including customer names.",
|
119 |
+
"hint": "Join accounts with customers and filter by balance < 0.",
|
120 |
+
"expected_sql": "SELECT a.account_id, c.name, a.account_type, a.balance FROM accounts a JOIN customers c ON a.customer_id = c.customer_id WHERE a.balance < 0;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Customers with no accounts",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List customers who do not have any accounts.",
|
127 |
+
"hint": "Use LEFT JOIN and check for NULL in accounts table.",
|
128 |
+
"expected_sql": "SELECT c.name FROM customers c LEFT JOIN accounts a ON c.customer_id = a.customer_id WHERE a.account_id IS NULL;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Account details with customer info",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show account details including customer name and city.",
|
135 |
+
"hint": "Join accounts and customers tables.",
|
136 |
+
"expected_sql": "SELECT a.account_id, a.account_type, a.balance, c.name, c.city FROM accounts a JOIN customers c ON a.customer_id = c.customer_id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Count accounts by city",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "Count the number of accounts for customers in each city.",
|
143 |
+
"hint": "Join customers and accounts, GROUP BY city.",
|
144 |
+
"expected_sql": "SELECT c.city, COUNT(a.account_id) AS account_count FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.city;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Accounts opened before 2023",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "List all accounts opened before 2023, including customer names.",
|
151 |
+
"hint": "Join accounts with customers and filter by opened_on.",
|
152 |
+
"expected_sql": "SELECT a.account_id, a.account_type, c.name FROM accounts a JOIN customers c ON a.customer_id = c.customer_id WHERE a.opened_on < '2023-01-01';"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Total balance by account type",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Calculate the total balance for each account type.",
|
159 |
+
"hint": "GROUP BY account_type and SUM balance.",
|
160 |
+
"expected_sql": "SELECT account_type, SUM(balance) AS total_balance FROM accounts GROUP BY account_type;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Customers with highest total balance",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Find the customer with the highest total balance across all their accounts.",
|
167 |
+
"hint": "Join tables, SUM balance, GROUP BY customer, and use LIMIT.",
|
168 |
+
"expected_sql": "SELECT c.name, SUM(a.balance) AS total_balance FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name ORDER BY total_balance DESC LIMIT 1;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Youngest customer with loan account",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Identify the youngest customer who has a Loan account.",
|
175 |
+
"hint": "Join tables, filter by account_type, and use MIN on age.",
|
176 |
+
"expected_sql": "SELECT c.name, c.age FROM customers c JOIN accounts a ON c.customer_id = a.customer_id WHERE a.account_type = 'Loan' ORDER BY c.age ASC LIMIT 1;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Account age in years",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Calculate the age of each account in years as of '2025-07-11'.",
|
183 |
+
"hint": "Use JULIANDAY to calculate date difference and divide by 365.25.",
|
184 |
+
"expected_sql": "SELECT account_id, account_type, ROUND((JULIANDAY('2025-07-11') - JULIANDAY(opened_on)) / 365.25, 1) AS account_age FROM accounts;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Customers with diverse account types",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "List customers who have more than one type of account.",
|
191 |
+
"hint": "Join tables, count distinct account types, and use HAVING.",
|
192 |
+
"expected_sql": "SELECT c.name, COUNT(DISTINCT a.account_type) AS type_count FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name HAVING type_count > 1;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Account openings by year",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the number of accounts opened each year.",
|
199 |
+
"hint": "Use STRFTIME to extract year and GROUP BY.",
|
200 |
+
"expected_sql": "SELECT STRFTIME('%Y', opened_on) AS year, COUNT(account_id) AS account_count FROM accounts GROUP BY year;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Customers with high negative balance",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "Find customers with a total balance less than -5000 across all accounts.",
|
207 |
+
"hint": "Join tables, SUM balance, and use HAVING clause.",
|
208 |
+
"expected_sql": "SELECT c.name, SUM(a.balance) AS total_balance FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name HAVING total_balance < -5000;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Oldest account per customer",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Show the earliest opened account for each customer.",
|
215 |
+
"hint": "Join tables, use MIN on opened_on, and GROUP BY customer.",
|
216 |
+
"expected_sql": "SELECT c.name, MIN(a.opened_on) AS earliest_opened FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.name;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Cities with high average balance",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "List cities where the average account balance is greater than 1000.",
|
223 |
+
"hint": "Join tables, GROUP BY city, and use HAVING clause.",
|
224 |
+
"expected_sql": "SELECT c.city, AVG(a.balance) AS avg_balance FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.city HAVING avg_balance > 1000;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Customers with no savings accounts",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List customers who do not have a Savings account.",
|
231 |
+
"hint": "Use NOT IN or LEFT JOIN to exclude customers with Savings accounts.",
|
232 |
+
"expected_sql": "SELECT c.name FROM customers c WHERE c.customer_id NOT IN (SELECT a.customer_id FROM accounts a WHERE a.account_type = 'Savings');"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Most common account type per city",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Find the most common account type in each city.",
|
239 |
+
"hint": "Join tables, group by city and account_type, use subquery or LIMIT.",
|
240 |
+
"expected_sql": "SELECT c.city, a.account_type, COUNT(a.account_id) AS account_count FROM customers c JOIN accounts a ON c.customer_id = a.customer_id GROUP BY c.city, a.account_type HAVING COUNT(a.account_id) = (SELECT MAX(account_count) FROM (SELECT COUNT(account_id) AS account_count FROM accounts a2 JOIN customers c2 ON a2.customer_id = c2.customer_id WHERE c2.city = c.city GROUP BY a2.account_type) AS counts);"
|
241 |
+
}
|
242 |
+
]
|
app/questions/college.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List all students",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and emails of all students.",
|
7 |
+
"hint": "Use SELECT on the students table.",
|
8 |
+
"expected_sql": "SELECT name, email FROM students;"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "List all courses",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show all courses with their names and credits.",
|
15 |
+
"hint": "Use SELECT on the courses table.",
|
16 |
+
"expected_sql": "SELECT name, credits FROM courses;"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "Find Spring 2023 enrollments",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Display student IDs and course IDs for enrollments in Spring 2023.",
|
23 |
+
"hint": "Use WHERE clause to filter by semester.",
|
24 |
+
"expected_sql": "SELECT student_id, course_id FROM enrollments WHERE semester = 'Spring 2023';"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Count total students",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Count the total number of students in the system.",
|
31 |
+
"hint": "Use COUNT() on the students table.",
|
32 |
+
"expected_sql": "SELECT COUNT(*) AS total_students FROM students;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List students in CSE department",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Show names and emails of students in the CSE department.",
|
39 |
+
"hint": "Use WHERE clause to filter by department.",
|
40 |
+
"expected_sql": "SELECT name, email FROM students WHERE department = 'CSE';"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "List distinct departments",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Retrieve all unique departments from the students table.",
|
47 |
+
"hint": "Use DISTINCT to avoid duplicate departments.",
|
48 |
+
"expected_sql": "SELECT DISTINCT department FROM students;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List courses in CSE department",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show all course names and instructors in the CSE department.",
|
55 |
+
"hint": "Use WHERE clause to filter by department.",
|
56 |
+
"expected_sql": "SELECT name, instructor FROM courses WHERE department = 'CSE';"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "List second-year students",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve names and emails of students in year 2.",
|
63 |
+
"hint": "Use WHERE clause to filter by year.",
|
64 |
+
"expected_sql": "SELECT name, email FROM students WHERE year = 2;"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "Find instructor for Data Structures",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show the instructor for the 'Data Structures' course.",
|
71 |
+
"hint": "Use WHERE clause to filter by course name.",
|
72 |
+
"expected_sql": "SELECT instructor FROM courses WHERE name = 'Data Structures';"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "List student IDs and names",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Show all student IDs and their corresponding names.",
|
79 |
+
"hint": "Use SELECT on the students table.",
|
80 |
+
"expected_sql": "SELECT student_id, name FROM students;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Students and their enrolled courses",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Display student names and the names of courses they are enrolled in.",
|
87 |
+
"hint": "Join students, enrollments, and courses tables.",
|
88 |
+
"expected_sql": "SELECT s.name AS student_name, c.name AS course_name FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Count students per course",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Show the number of students enrolled in each course.",
|
95 |
+
"hint": "Join enrollments with courses and GROUP BY course name.",
|
96 |
+
"expected_sql": "SELECT c.name, COUNT(e.student_id) AS student_count FROM enrollments e JOIN courses c ON e.course_id = c.course_id GROUP BY c.name;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Students with A grades",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "List names of students who received an 'A' grade in any course.",
|
103 |
+
"hint": "Join enrollments with students and filter by grade.",
|
104 |
+
"expected_sql": "SELECT DISTINCT s.name FROM enrollments e JOIN students s ON e.student_id = s.student_id WHERE e.grade = 'A';"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Courses with no enrollments",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Find courses that have no students enrolled.",
|
111 |
+
"hint": "Use LEFT JOIN and filter for NULL enrollment IDs.",
|
112 |
+
"expected_sql": "SELECT c.name FROM courses c LEFT JOIN enrollments e ON c.course_id = e.course_id WHERE e.enrollment_id IS NULL;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Grades for Operating Systems",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "Show student names and grades for the 'Operating Systems' course.",
|
119 |
+
"hint": "Join enrollments, students, and courses, filter by course name.",
|
120 |
+
"expected_sql": "SELECT s.name, e.grade FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id WHERE c.name = 'Operating Systems';"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Courses taught by Dr. Anil Kapoor",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List all courses taught by Dr. Anil Kapoor.",
|
127 |
+
"hint": "Use WHERE clause to filter by instructor.",
|
128 |
+
"expected_sql": "SELECT name FROM courses WHERE instructor = 'Dr. Anil Kapoor';"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Students with multiple enrollments",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Find students enrolled in more than one course.",
|
135 |
+
"hint": "Group by student name and use HAVING clause.",
|
136 |
+
"expected_sql": "SELECT s.name, COUNT(e.course_id) AS course_count FROM enrollments e JOIN students s ON e.student_id = s.student_id GROUP BY s.name HAVING COUNT(e.course_id) > 1;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Total credits per student",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "Calculate the total credits each student is enrolled in.",
|
143 |
+
"hint": "Join enrollments with courses and SUM credits.",
|
144 |
+
"expected_sql": "SELECT s.name, SUM(c.credits) AS total_credits FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id GROUP BY s.name;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Courses per department",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "Count the number of courses offered by each department.",
|
151 |
+
"hint": "Group by department in the courses table.",
|
152 |
+
"expected_sql": "SELECT department, COUNT(course_id) AS course_count FROM courses GROUP BY department;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Student enrollment semesters",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "List student names and the semesters they are enrolled in.",
|
159 |
+
"hint": "Join enrollments with students.",
|
160 |
+
"expected_sql": "SELECT s.name, e.semester FROM enrollments e JOIN students s ON e.student_id = s.student_id;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Average GPA per course",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Calculate the average GPA per course, mapping grades to numeric values (A=4.0, A-=3.7, B+=3.3, B=3.0).",
|
167 |
+
"hint": "Use CASE to map grades and AVG for calculation.",
|
168 |
+
"expected_sql": "SELECT c.name, AVG(CASE e.grade WHEN 'A' THEN 4.0 WHEN 'A-' THEN 3.7 WHEN 'B+' THEN 3.3 WHEN 'B' THEN 3.0 ELSE 0 END) AS avg_gpa FROM enrollments e JOIN courses c ON e.course_id = c.course_id GROUP BY c.name;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Top student per course",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Find the student with the highest grade in each course.",
|
175 |
+
"hint": "Use a subquery to find the maximum grade per course.",
|
176 |
+
"expected_sql": "SELECT s.name, c.name AS course_name, e.grade FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id WHERE (e.course_id, e.grade) IN (SELECT course_id, MAX(grade) FROM enrollments GROUP BY course_id);"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Average credits per department",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Calculate the average total credits per student in each department.",
|
183 |
+
"hint": "Group by student and department, then average credits.",
|
184 |
+
"expected_sql": "SELECT s.department, AVG(total_credits) AS avg_credits FROM (SELECT s.student_id, s.department, SUM(c.credits) AS total_credits FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id GROUP BY s.student_id, s.department) AS temp GROUP BY department;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Students not enrolled",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "List students who are not enrolled in any course.",
|
191 |
+
"hint": "Use LEFT JOIN and filter for NULL enrollments.",
|
192 |
+
"expected_sql": "SELECT s.name FROM students s LEFT JOIN enrollments e ON s.student_id = e.student_id WHERE e.enrollment_id IS NULL;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Course with highest average GPA",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Find the course with the highest average GPA.",
|
199 |
+
"hint": "Map grades to GPA, group by course, and use LIMIT.",
|
200 |
+
"expected_sql": "SELECT c.name, AVG(CASE e.grade WHEN 'A' THEN 4.0 WHEN 'A-' THEN 3.7 WHEN 'B+' THEN 3.3 WHEN 'B' THEN 3.0 ELSE 0 END) AS avg_gpa FROM enrollments e JOIN courses c ON e.course_id = c.course_id GROUP BY c.name ORDER BY avg_gpa DESC LIMIT 1;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Students enrolled in all CSE courses",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "List students who have enrolled in every CSE department course.",
|
207 |
+
"hint": "Compare count of CSE courses with enrolled courses using HAVING.",
|
208 |
+
"expected_sql": "SELECT s.name FROM enrollments e JOIN students s ON e.student_id = s.student_id JOIN courses c ON e.course_id = c.course_id WHERE c.department = 'CSE' GROUP BY s.name HAVING COUNT(DISTINCT c.course_id) = (SELECT COUNT(*) FROM courses WHERE department = 'CSE');"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Courses with multi-department students",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Find courses with students from more than one department.",
|
215 |
+
"hint": "Group by course and count distinct departments.",
|
216 |
+
"expected_sql": "SELECT c.name FROM enrollments e JOIN courses c ON e.course_id = c.course_id JOIN students s ON e.student_id = s.student_id GROUP BY c.name HAVING COUNT(DISTINCT s.department) > 1;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Instructor average GPA",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Calculate the average GPA for students in courses taught by each instructor.",
|
223 |
+
"hint": "Join tables, map grades to GPA, and group by instructor.",
|
224 |
+
"expected_sql": "SELECT c.instructor, AVG(CASE e.grade WHEN 'A' THEN 4.0 WHEN 'A-' THEN 3.7 WHEN 'B+' THEN 3.3 WHEN 'B' THEN 3.0 ELSE 0 END) AS avg_gpa FROM enrollments e JOIN courses c ON e.course_id = c.course_id GROUP BY c.instructor;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Students with consistent grades",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List students who received the same grade in multiple courses.",
|
231 |
+
"hint": "Group by student and grade, filter for count > 1.",
|
232 |
+
"expected_sql": "SELECT s.name, e.grade FROM enrollments e JOIN students s ON e.student_id = s.student_id GROUP BY s.name, e.grade HAVING COUNT(*) > 1;"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Highest credit course per department",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Find the course with the highest credits in each department.",
|
239 |
+
"hint": "Use a window function or subquery to rank courses by credits.",
|
240 |
+
"expected_sql": "SELECT department, name, credits FROM (SELECT department, name, credits, RANK() OVER (PARTITION BY department ORDER BY credits DESC) AS rnk FROM courses) AS ranked WHERE rnk = 1;"
|
241 |
+
}
|
242 |
+
]
|
app/questions/e_commerce.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List electronics products",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and sale amounts of all products in the Electronics category.",
|
7 |
+
"hint": "Use WHERE clause to filter by category.",
|
8 |
+
"expected_sql": "SELECT product_name, sale_amount FROM sales WHERE category = 'Electronics';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find sales after specific date",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show all sales that occurred after January 5, 2023.",
|
15 |
+
"hint": "Use WHERE clause with date comparison.",
|
16 |
+
"expected_sql": "SELECT product_name, sale_date FROM sales WHERE sale_date > '2023-01-05';"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List orders by customer",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve all orders placed by customer with ID 2001.",
|
23 |
+
"hint": "Use WHERE clause to filter by customer_id.",
|
24 |
+
"expected_sql": "SELECT order_id, product_id, order_date FROM orders WHERE customer_id = 2001;"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Find high-value sales",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show products with a sale amount greater than 1000.",
|
31 |
+
"hint": "Use WHERE clause to filter by sale_amount.",
|
32 |
+
"expected_sql": "SELECT product_name, sale_amount FROM sales WHERE sale_amount > 1000;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique categories",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique product categories from the sales table.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate categories.",
|
40 |
+
"expected_sql": "SELECT DISTINCT category FROM sales;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Find orders with quantity greater than 1",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Show order details where the quantity is greater than 1.",
|
47 |
+
"hint": "Use WHERE clause to filter by quantity.",
|
48 |
+
"expected_sql": "SELECT order_id, product_id, quantity FROM orders WHERE quantity > 1;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List sales by date",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show all sales ordered by sale date.",
|
55 |
+
"hint": "Use ORDER BY clause on sale_date.",
|
56 |
+
"expected_sql": "SELECT product_name, sale_date FROM sales ORDER BY sale_date;"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Find accessories sales",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve all sales from the Accessories category.",
|
63 |
+
"hint": "Use WHERE clause to filter by category.",
|
64 |
+
"expected_sql": "SELECT product_name, sale_amount FROM sales WHERE category = 'Accessories';"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List order quantities",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show the quantity for each order.",
|
71 |
+
"hint": "Select quantity from orders table.",
|
72 |
+
"expected_sql": "SELECT order_id, quantity FROM orders;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "Find sales with low amounts",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Retrieve products with a sale amount less than 100.",
|
79 |
+
"hint": "Use WHERE clause to filter by sale_amount.",
|
80 |
+
"expected_sql": "SELECT product_name, sale_amount FROM sales WHERE sale_amount < 100;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Total revenue by category",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Calculate the total sale amount for each product category.",
|
87 |
+
"hint": "Use GROUP BY on category and SUM on sale_amount.",
|
88 |
+
"expected_sql": "SELECT category, SUM(sale_amount) AS total_revenue FROM sales GROUP BY category;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Count orders per product",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Show the number of orders for each product.",
|
95 |
+
"hint": "Join sales and orders, then GROUP BY product_name.",
|
96 |
+
"expected_sql": "SELECT s.product_name, COUNT(o.order_id) AS order_count FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.product_name;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Average sale amount per product",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Find the average sale amount for each product name.",
|
103 |
+
"hint": "Use GROUP BY on product_name and AVG on sale_amount.",
|
104 |
+
"expected_sql": "SELECT product_name, AVG(sale_amount) AS avg_sale FROM sales GROUP BY product_name;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Customer order count",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Show the number of orders placed by each customer.",
|
111 |
+
"hint": "Use GROUP BY on customer_id and COUNT.",
|
112 |
+
"expected_sql": "SELECT customer_id, COUNT(order_id) AS order_count FROM orders GROUP BY customer_id;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "High quantity orders",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "List orders with a total quantity greater than 2.",
|
119 |
+
"hint": "Use GROUP BY on order_id and HAVING clause.",
|
120 |
+
"expected_sql": "SELECT order_id, SUM(quantity) AS total_quantity FROM orders GROUP BY order_id HAVING total_quantity > 2;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Products not ordered",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List products that have not been ordered.",
|
127 |
+
"hint": "Use LEFT JOIN and check for NULL in orders table.",
|
128 |
+
"expected_sql": "SELECT s.product_name FROM sales s LEFT JOIN orders o ON s.id = o.product_id WHERE o.order_id IS NULL;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Order details with products",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show order details including product names and quantities.",
|
135 |
+
"hint": "Join sales and orders tables.",
|
136 |
+
"expected_sql": "SELECT o.order_id, s.product_name, o.quantity FROM orders o JOIN sales s ON o.product_id = s.id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Total quantity per customer",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "Calculate the total quantity of items ordered by each customer.",
|
143 |
+
"hint": "Use GROUP BY on customer_id and SUM on quantity.",
|
144 |
+
"expected_sql": "SELECT customer_id, SUM(quantity) AS total_quantity FROM orders GROUP BY customer_id;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Orders by date range",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "List all orders placed between January 1, 2023, and January 5, 2023.",
|
151 |
+
"hint": "Use BETWEEN clause for date range.",
|
152 |
+
"expected_sql": "SELECT order_id, customer_id, order_date FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-01-05';"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Most ordered product category",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Find the category with the highest number of orders.",
|
159 |
+
"hint": "Join sales and orders, group by category, and use LIMIT.",
|
160 |
+
"expected_sql": "SELECT s.category, COUNT(o.order_id) AS order_count FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.category ORDER BY order_count DESC LIMIT 1;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Total revenue per customer",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Calculate the total revenue (sale_amount * quantity) generated by each customer.",
|
167 |
+
"hint": "Join sales and orders, multiply sale_amount by quantity, and group by customer_id.",
|
168 |
+
"expected_sql": "SELECT o.customer_id, SUM(s.sale_amount * o.quantity) AS total_revenue FROM orders o JOIN sales s ON o.product_id = s.id GROUP BY o.customer_id;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Most expensive ordered product",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Find the product with the highest sale amount that was ordered.",
|
175 |
+
"hint": "Join sales and orders, use ORDER BY and LIMIT.",
|
176 |
+
"expected_sql": "SELECT s.product_name, s.sale_amount FROM sales s JOIN orders o ON s.id = o.product_id ORDER BY s.sale_amount DESC LIMIT 1;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Daily revenue trend",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Show the total revenue (sale_amount * quantity) per day for all orders.",
|
183 |
+
"hint": "Join tables, group by order_date, and calculate revenue.",
|
184 |
+
"expected_sql": "SELECT o.order_date, SUM(s.sale_amount * o.quantity) AS daily_revenue FROM orders o JOIN sales s ON o.product_id = s.id GROUP BY o.order_date;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Customers with high-value orders",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "Identify customers whose total order value exceeds 2000.",
|
191 |
+
"hint": "Join tables, calculate total value, and use HAVING clause.",
|
192 |
+
"expected_sql": "SELECT o.customer_id, SUM(s.sale_amount * o.quantity) AS total_value FROM orders o JOIN sales s ON o.product_id = s.id GROUP BY o.customer_id HAVING total_value > 2000;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Products ordered by multiple customers",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Find products that have been ordered by more than one distinct customer.",
|
199 |
+
"hint": "Join sales and orders, use COUNT and DISTINCT on customer_id, and group by product.",
|
200 |
+
"expected_sql": "SELECT s.product_name, COUNT(DISTINCT o.customer_id) AS customer_count FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.product_name HAVING customer_count > 1;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Revenue contribution by category",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "Calculate the total revenue (sale_amount * quantity) for each product category.",
|
207 |
+
"hint": "Join tables, multiply sale_amount by quantity, and group by category.",
|
208 |
+
"expected_sql": "SELECT s.category, SUM(s.sale_amount * o.quantity) AS total_revenue FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.category;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Earliest and latest order per customer",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Show the earliest and latest order dates for each customer.",
|
215 |
+
"hint": "Use MIN and MAX on order_date, grouped by customer_id.",
|
216 |
+
"expected_sql": "SELECT customer_id, MIN(order_date) AS earliest_order, MAX(order_date) AS latest_order FROM orders GROUP BY customer_id;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Products with high total quantity",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Find products with a total ordered quantity greater than 2.",
|
223 |
+
"hint": "Join sales and orders, group by product, and use HAVING clause.",
|
224 |
+
"expected_sql": "SELECT s.product_name, SUM(o.quantity) AS total_quantity FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.product_name HAVING total_quantity > 2;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Customers who ordered all electronics",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List customers who have ordered every product in the Electronics category.",
|
231 |
+
"hint": "Count distinct Electronics products per customer and compare with total Electronics products.",
|
232 |
+
"expected_sql": "SELECT o.customer_id FROM orders o JOIN sales s ON o.product_id = s.id WHERE s.category = 'Electronics' GROUP BY o.customer_id HAVING COUNT(DISTINCT s.id) = (SELECT COUNT(*) FROM sales WHERE category = 'Electronics');"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Most frequent customer by product",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Find the customer who ordered each product the most times.",
|
239 |
+
"hint": "Join tables, group by product and customer, use subquery to find max orders.",
|
240 |
+
"expected_sql": "SELECT s.product_name, o.customer_id, COUNT(o.order_id) AS order_count FROM sales s JOIN orders o ON s.id = o.product_id GROUP BY s.product_name, o.customer_id HAVING COUNT(o.order_id) = (SELECT MAX(order_count) FROM (SELECT COUNT(order_id) AS order_count FROM orders o2 WHERE o2.product_id = s.id GROUP BY o2.customer_id) AS counts);"
|
241 |
+
}
|
242 |
+
]
|
app/questions/hospital.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List female patients",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and contact details of all female patients.",
|
7 |
+
"hint": "Use a WHERE clause to filter by gender.",
|
8 |
+
"expected_sql": "SELECT name, contact FROM patients WHERE gender = 'Female';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find appointments on specific date",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show all appointments scheduled on July 10, 2023.",
|
15 |
+
"hint": "Use a WHERE clause with date comparison.",
|
16 |
+
"expected_sql": "SELECT appointment_id, patient_id, doctor_id, appointment_date FROM appointments WHERE appointment_date = '2023-07-10';"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List cardiologists",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve the names and contact details of all doctors with the Cardiologist specialty.",
|
23 |
+
"hint": "Use a WHERE clause to filter by specialty.",
|
24 |
+
"expected_sql": "SELECT name, contact FROM doctors WHERE specialty = 'Cardiologist';"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Patients born before 1995",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show the names and dates of birth of patients born before January 1, 1995.",
|
31 |
+
"hint": "Use a WHERE clause with date comparison on dob.",
|
32 |
+
"expected_sql": "SELECT name, dob FROM patients WHERE dob < '1995-01-01';"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique diagnoses",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique diagnoses from the appointments table.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate diagnoses.",
|
40 |
+
"expected_sql": "SELECT DISTINCT diagnosis FROM appointments;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Appointments for doctor ID 1",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Show all appointments for the doctor with ID 1.",
|
47 |
+
"hint": "Use a WHERE clause to filter by doctor_id.",
|
48 |
+
"expected_sql": "SELECT appointment_id, patient_id, appointment_date FROM appointments WHERE doctor_id = 1;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List patient names and genders",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show all patient names and their genders.",
|
55 |
+
"hint": "Select name and gender from the patients table.",
|
56 |
+
"expected_sql": "SELECT name, gender FROM patients;"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Appointments in August 2023",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve all appointments scheduled in August 2023.",
|
63 |
+
"hint": "Use a WHERE clause with LIKE for appointment_date.",
|
64 |
+
"expected_sql": "SELECT appointment_id, patient_id, doctor_id, appointment_date FROM appointments WHERE appointment_date LIKE '2023-08%';"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List doctors and specialties",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show all doctor names and their specialties.",
|
71 |
+
"hint": "Select name and specialty from the doctors table.",
|
72 |
+
"expected_sql": "SELECT name, specialty FROM doctors;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "Patients with Routine Checkup",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Retrieve the names of patients with a diagnosis of 'Routine Checkup'.",
|
79 |
+
"hint": "Join patients and appointments, filter by diagnosis.",
|
80 |
+
"expected_sql": "SELECT p.name FROM patients p JOIN appointments a ON p.patient_id = a.patient_id WHERE a.diagnosis = 'Routine Checkup';"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Appointments per doctor",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Show the number of appointments for each doctor, including those with zero appointments.",
|
87 |
+
"hint": "Use a LEFT JOIN and GROUP BY doctor name.",
|
88 |
+
"expected_sql": "SELECT d.name, COUNT(a.appointment_id) AS appointment_count FROM doctors d LEFT JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.name;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Patients with multiple appointments",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Find patients who have more than one appointment.",
|
95 |
+
"hint": "Join patients and appointments, use GROUP BY and HAVING clause.",
|
96 |
+
"expected_sql": "SELECT p.name, COUNT(a.appointment_id) AS appointment_count FROM patients p JOIN appointments a ON p.patient_id = a.patient_id GROUP BY p.name HAVING COUNT(a.appointment_id) > 1;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Appointments by doctor specialty",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Count the number of appointments for each doctor specialty.",
|
103 |
+
"hint": "Join doctors and appointments, then GROUP BY specialty.",
|
104 |
+
"expected_sql": "SELECT d.specialty, COUNT(a.appointment_id) AS total_appointments FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.specialty;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Patient and doctor appointment details",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Show patient names, doctor names, and appointment dates for all appointments.",
|
111 |
+
"hint": "Join patients, doctors, and appointments tables.",
|
112 |
+
"expected_sql": "SELECT p.name AS patient_name, d.name AS doctor_name, a.appointment_date FROM appointments a JOIN patients p ON a.patient_id = p.patient_id JOIN doctors d ON a.doctor_id = d.doctor_id;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Diagnoses by patient gender",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "Count the number of appointments for each diagnosis, grouped by patient gender.",
|
119 |
+
"hint": "Join patients and appointments, GROUP BY gender and diagnosis.",
|
120 |
+
"expected_sql": "SELECT p.gender, a.diagnosis, COUNT(a.appointment_id) AS appointment_count FROM patients p JOIN appointments a ON p.patient_id = a.patient_id GROUP BY p.gender, a.diagnosis;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Doctors with no appointments",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List doctors who have not had any appointments.",
|
127 |
+
"hint": "Use a LEFT JOIN and check for NULL in the appointments table.",
|
128 |
+
"expected_sql": "SELECT d.name FROM doctors d LEFT JOIN appointments a ON d.doctor_id = a.doctor_id WHERE a.appointment_id IS NULL;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Patients seen by multiple doctors",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Find patients who have appointments with more than one distinct doctor.",
|
135 |
+
"hint": "Join patients and appointments, group by patient, and use HAVING clause.",
|
136 |
+
"expected_sql": "SELECT p.name, COUNT(DISTINCT a.doctor_id) AS doctor_count FROM patients p JOIN appointments a ON p.patient_id = a.patient_id GROUP BY p.name HAVING COUNT(DISTINCT a.doctor_id) > 1;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Appointments in July 2023",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "List all appointments between July 1, 2023, and July 31, 2023.",
|
143 |
+
"hint": "Use a BETWEEN clause for the date range.",
|
144 |
+
"expected_sql": "SELECT appointment_id, patient_id, doctor_id, appointment_date FROM appointments WHERE appointment_date BETWEEN '2023-07-01' AND '2023-07-31';"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Most common diagnosis",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "Find the diagnosis with the highest number of appointments.",
|
151 |
+
"hint": "Group by diagnosis, count appointments, and use LIMIT.",
|
152 |
+
"expected_sql": "SELECT diagnosis, COUNT(appointment_id) AS diagnosis_count FROM appointments GROUP BY diagnosis ORDER BY diagnosis_count DESC LIMIT 1;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Unique patients by specialty",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Show the number of unique patients seen by each doctor specialty.",
|
159 |
+
"hint": "Join doctors and appointments, use COUNT and DISTINCT, group by specialty.",
|
160 |
+
"expected_sql": "SELECT d.specialty, COUNT(DISTINCT a.patient_id) AS unique_patients FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.specialty;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Patient age at appointment",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Calculate the age of each patient at the time of their appointment.",
|
167 |
+
"hint": "Join patients and appointments, use JULIANDAY for age calculation.",
|
168 |
+
"expected_sql": "SELECT p.name, a.appointment_date, ROUND((JULIANDAY(a.appointment_date) - JULIANDAY(p.dob)) / 365.25, 1) AS age FROM patients p JOIN appointments a ON p.patient_id = a.patient_id;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Most active doctor",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Find the doctor with the highest number of appointments.",
|
175 |
+
"hint": "Join doctors and appointments, group by doctor name, and use LIMIT.",
|
176 |
+
"expected_sql": "SELECT d.name, COUNT(a.appointment_id) AS appointment_count FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.name ORDER BY appointment_count DESC LIMIT 1;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Patients seen by Cardiologist",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "List all patients who have seen a Cardiologist, including their diagnoses.",
|
183 |
+
"hint": "Join all tables and filter by doctor specialty.",
|
184 |
+
"expected_sql": "SELECT DISTINCT p.name, a.diagnosis FROM patients p JOIN appointments a ON p.patient_id = a.patient_id JOIN doctors d ON a.doctor_id = d.doctor_id WHERE d.specialty = 'Cardiologist';"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Diagnosis frequency by doctor",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "Show the number of times each diagnosis was made by each doctor.",
|
191 |
+
"hint": "Join doctors and appointments, group by doctor name and diagnosis.",
|
192 |
+
"expected_sql": "SELECT d.name, a.diagnosis, COUNT(a.appointment_id) AS diagnosis_count FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.name, a.diagnosis;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Appointments per month",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the number of appointments per month in 2023.",
|
199 |
+
"hint": "Use STRFTIME to extract the month and GROUP BY.",
|
200 |
+
"expected_sql": "SELECT STRFTIME('%Y-%m', appointment_date) AS month, COUNT(appointment_id) AS appointment_count FROM appointments GROUP BY month;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Youngest patient per doctor",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "Find the youngest patient seen by each doctor.",
|
207 |
+
"hint": "Join tables, calculate age using JULIANDAY, and group by doctor.",
|
208 |
+
"expected_sql": "SELECT d.name, p.name AS patient_name, MIN(ROUND((JULIANDAY(a.appointment_date) - JULIANDAY(p.dob)) / 365.25, 1)) AS age FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id JOIN patients p ON a.patient_id = p.patient_id GROUP BY d.name;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Patients with multiple specialties",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "List patients who have seen doctors from more than one specialty.",
|
215 |
+
"hint": "Join tables, count distinct specialties, and use HAVING clause.",
|
216 |
+
"expected_sql": "SELECT p.name, COUNT(DISTINCT d.specialty) AS specialty_count FROM patients p JOIN appointments a ON p.patient_id = a.patient_id JOIN doctors d ON a.doctor_id = d.doctor_id GROUP BY p.name HAVING COUNT(DISTINCT d.specialty) > 1;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Unique diagnoses per doctor",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Show the number of unique diagnoses made by each doctor.",
|
223 |
+
"hint": "Join doctors and appointments, use COUNT and DISTINCT, group by doctor name.",
|
224 |
+
"expected_sql": "SELECT d.name, COUNT(DISTINCT a.diagnosis) AS unique_diagnoses FROM doctors d JOIN appointments a ON d.doctor_id = a.doctor_id GROUP BY d.name;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Patients not seen by Neurologist",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List patients who have never been seen by a Neurologist.",
|
231 |
+
"hint": "Use NOT IN or LEFT JOIN to exclude patients with Neurologist appointments.",
|
232 |
+
"expected_sql": "SELECT p.name FROM patients p WHERE p.patient_id NOT IN (SELECT a.patient_id FROM appointments a JOIN doctors d ON a.doctor_id = d.doctor_id WHERE d.specialty = 'Neurologist');"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Earliest and latest appointment per patient",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Show the earliest and latest appointment dates for each patient with appointments.",
|
239 |
+
"hint": "Join patients and appointments, use MIN and MAX on appointment_date.",
|
240 |
+
"expected_sql": "SELECT p.name, MIN(a.appointment_date) AS earliest_appointment, MAX(a.appointment_date) AS latest_appointment FROM patients p JOIN appointments a ON p.patient_id = a.patient_id GROUP BY p.name;"
|
241 |
+
}
|
242 |
+
]
|
app/questions/inventory.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List electronics products",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and prices of all products in the Electronics category.",
|
7 |
+
"hint": "Use a WHERE clause to filter by category.",
|
8 |
+
"expected_sql": "SELECT product_name, price FROM inventory WHERE category = 'Electronics';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find products with low stock",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show products with stock less than 10 units.",
|
15 |
+
"hint": "Use a WHERE clause to filter by stock.",
|
16 |
+
"expected_sql": "SELECT product_name, stock FROM inventory WHERE stock < 10;"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List restocks for product ID 1",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve all restock details for the product with ID 1.",
|
23 |
+
"hint": "Use a WHERE clause to filter by product_id.",
|
24 |
+
"expected_sql": "SELECT restock_id, restock_date, quantity, supplier FROM restocks WHERE product_id = 1;"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Find products by price range",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show products with prices between 50 and 500, inclusive.",
|
31 |
+
"hint": "Use a BETWEEN clause for the price range.",
|
32 |
+
"expected_sql": "SELECT product_name, price FROM inventory WHERE price BETWEEN 50 AND 500;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique suppliers",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique suppliers from the restocks table.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate suppliers.",
|
40 |
+
"expected_sql": "SELECT DISTINCT supplier FROM restocks;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Products with no restocks",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "List products that have never been restocked.",
|
47 |
+
"hint": "Use a LEFT JOIN and check for NULL in the restocks table.",
|
48 |
+
"expected_sql": "SELECT i.product_name FROM inventory i LEFT JOIN restocks r ON i.product_id = r.product_id WHERE r.restock_id IS NULL;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "Restocks in January 2023",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show all restocks that occurred in January 2023.",
|
55 |
+
"hint": "Use a WHERE clause with LIKE for restock_date.",
|
56 |
+
"expected_sql": "SELECT restock_id, product_id, restock_date, quantity FROM restocks WHERE restock_date LIKE '2023-01%';"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "List products and categories",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Show all product names and their categories.",
|
63 |
+
"hint": "Select product_name and category from the inventory table.",
|
64 |
+
"expected_sql": "SELECT product_name, category FROM inventory;"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "Find high-priced products",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Retrieve products with a price greater than 1000.",
|
71 |
+
"hint": "Use a WHERE clause to filter by price.",
|
72 |
+
"expected_sql": "SELECT product_name, price FROM inventory WHERE price > 1000;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "List restock quantities",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Show the quantity restocked for each restock event.",
|
79 |
+
"hint": "Select restock_id and quantity from the restocks table.",
|
80 |
+
"expected_sql": "SELECT restock_id, quantity FROM restocks;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Total stock per category",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Calculate the total stock for each product category.",
|
87 |
+
"hint": "Use GROUP BY on category and SUM on stock.",
|
88 |
+
"expected_sql": "SELECT category, SUM(stock) AS total_stock FROM inventory GROUP BY category;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Average product price by category",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Find the average price of products in each category.",
|
95 |
+
"hint": "Use GROUP BY on category and AVG on price.",
|
96 |
+
"expected_sql": "SELECT category, AVG(price) AS avg_price FROM inventory GROUP BY category;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Restocks by supplier",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Count the number of restock events for each supplier.",
|
103 |
+
"hint": "Use GROUP BY on supplier and COUNT.",
|
104 |
+
"expected_sql": "SELECT supplier, COUNT(restock_id) AS restock_count FROM restocks GROUP BY supplier;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Total restock quantity per product",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Show the total quantity restocked for each product.",
|
111 |
+
"hint": "Join inventory and restocks, then GROUP BY product_name.",
|
112 |
+
"expected_sql": "SELECT i.product_name, SUM(r.quantity) AS total_restocked FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Products with multiple restocks",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "List products that have been restocked more than once.",
|
119 |
+
"hint": "Use GROUP BY on product_name and HAVING clause.",
|
120 |
+
"expected_sql": "SELECT i.product_name, COUNT(r.restock_id) AS restock_count FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name HAVING COUNT(r.restock_id) > 1;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Total inventory value by category",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "Calculate the total value of inventory (price * stock) for each category.",
|
127 |
+
"hint": "Use GROUP BY on category and SUM on price * stock.",
|
128 |
+
"expected_sql": "SELECT category, SUM(price * stock) AS total_value FROM inventory GROUP BY category;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Restock details with products",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show restock details including product names, quantities, and suppliers.",
|
135 |
+
"hint": "Join inventory and restocks tables.",
|
136 |
+
"expected_sql": "SELECT r.restock_id, i.product_name, r.restock_date, r.quantity, r.supplier FROM inventory i JOIN restocks r ON i.product_id = r.product_id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Products with high stock",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "List products with stock greater than 20, ordered by stock descending.",
|
143 |
+
"hint": "Use WHERE and ORDER BY clauses.",
|
144 |
+
"expected_sql": "SELECT product_name, stock FROM inventory WHERE stock > 20 ORDER BY stock DESC;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Suppliers with large restocks",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "Find suppliers who have restocked quantities greater than 10 in a single restock event.",
|
151 |
+
"hint": "Use WHERE clause on quantity and select DISTINCT supplier.",
|
152 |
+
"expected_sql": "SELECT DISTINCT supplier FROM restocks WHERE quantity > 10;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Restock frequency by product",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Show the number of restock events for each product, including those with zero restocks.",
|
159 |
+
"hint": "Use a LEFT JOIN and GROUP BY product_name.",
|
160 |
+
"expected_sql": "SELECT i.product_name, COUNT(r.restock_id) AS restock_count FROM inventory i LEFT JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Most expensive restocked product",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Find the product with the highest price that has been restocked.",
|
167 |
+
"hint": "Join inventory and restocks, use ORDER BY and LIMIT.",
|
168 |
+
"expected_sql": "SELECT i.product_name, i.price FROM inventory i JOIN restocks r ON i.product_id = r.product_id ORDER BY i.price DESC LIMIT 1;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Total restock value per supplier",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Calculate the total value of restocks (price * quantity) for each supplier.",
|
175 |
+
"hint": "Join tables, multiply price by quantity, and group by supplier.",
|
176 |
+
"expected_sql": "SELECT r.supplier, SUM(i.price * r.quantity) AS total_restock_value FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY r.supplier;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Latest restock per product",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Show the most recent restock date for each product that has been restocked.",
|
183 |
+
"hint": "Use GROUP BY on product_name and MAX on restock_date.",
|
184 |
+
"expected_sql": "SELECT i.product_name, MAX(r.restock_date) AS latest_restock FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Low stock high-value products",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "Identify products with stock less than 10 and price greater than 500.",
|
191 |
+
"hint": "Use multiple conditions in the WHERE clause.",
|
192 |
+
"expected_sql": "SELECT product_name, price, stock FROM inventory WHERE stock < 10 AND price > 500;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Restock trend by month",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the total quantity restocked per month in 2023.",
|
199 |
+
"hint": "Use STRFTIME to extract the month and GROUP BY.",
|
200 |
+
"expected_sql": "SELECT STRFTIME('%Y-%m', restock_date) AS month, SUM(quantity) AS total_quantity FROM restocks GROUP BY month;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Products with high restock value",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "Find products where the total restock value (price * quantity) exceeds 5000.",
|
207 |
+
"hint": "Join tables, calculate value, and use HAVING clause.",
|
208 |
+
"expected_sql": "SELECT i.product_name, SUM(i.price * r.quantity) AS total_restock_value FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name HAVING SUM(i.price * r.quantity) > 5000;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Supplier product diversity",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Count the number of unique products each supplier has restocked.",
|
215 |
+
"hint": "Use COUNT and DISTINCT on product_id, grouped by supplier.",
|
216 |
+
"expected_sql": "SELECT r.supplier, COUNT(DISTINCT r.product_id) AS unique_products FROM restocks r GROUP BY r.supplier;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Overstocked products",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Identify products where the total restocked quantity exceeds the current stock by more than 10.",
|
223 |
+
"hint": "Join tables, compare SUM(quantity) with stock, and use HAVING.",
|
224 |
+
"expected_sql": "SELECT i.product_name, i.stock, SUM(r.quantity) AS total_restocked FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name, i.stock HAVING SUM(r.quantity) > i.stock + 10;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Suppliers restocking all electronics",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List suppliers who have restocked every product in the Electronics category.",
|
231 |
+
"hint": "Count distinct Electronics products per supplier and compare with total Electronics products.",
|
232 |
+
"expected_sql": "SELECT r.supplier FROM restocks r JOIN inventory i ON r.product_id = i.product_id WHERE i.category = 'Electronics' GROUP BY r.supplier HAVING COUNT(DISTINCT r.product_id) = (SELECT COUNT(*) FROM inventory WHERE category = 'Electronics');"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Earliest and latest restock per supplier",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Show the earliest and latest restock dates for each supplier.",
|
239 |
+
"hint": "Use MIN and MAX on restock_date, grouped by supplier.",
|
240 |
+
"expected_sql": "SELECT supplier, MIN(restock_date) AS earliest_restock, MAX(restock_date) AS latest_restock FROM restocks GROUP BY supplier;"
|
241 |
+
}
|
242 |
+
]
|
app/questions/library.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List all science fiction books",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the titles and authors of all books in the Science Fiction genre.",
|
7 |
+
"hint": "Use a WHERE clause to filter by genre.",
|
8 |
+
"expected_sql": "SELECT title, author FROM books WHERE genre = 'Science Fiction';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find books with low availability",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show books with fewer than 2 available copies.",
|
15 |
+
"hint": "Use a WHERE clause to filter by available_copies.",
|
16 |
+
"expected_sql": "SELECT title, available_copies FROM books WHERE available_copies < 2;"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List loans for user ID 101",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve all loan details for the user with ID 101.",
|
23 |
+
"hint": "Use a WHERE clause to filter by user_id.",
|
24 |
+
"expected_sql": "SELECT loan_id, book_id, issue_date, due_date FROM loans WHERE user_id = 101;"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Find books published after 2000",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show titles and publication years of books published after 2000.",
|
31 |
+
"hint": "Use a WHERE clause with year comparison.",
|
32 |
+
"expected_sql": "SELECT title, year FROM books WHERE year > 2000;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique genres",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique genres from the books table.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate genres.",
|
40 |
+
"expected_sql": "SELECT DISTINCT genre FROM books;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Find overdue loans",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Show loans that are overdue as of '2023-03-01' (due_date before '2023-03-01' and not returned).",
|
47 |
+
"hint": "Use a WHERE clause to check due_date and NULL return_date.",
|
48 |
+
"expected_sql": "SELECT loan_id, book_id, due_date FROM loans WHERE due_date < '2023-03-01' AND return_date IS NULL;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List users joined after 2020",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show names and emails of users who joined after December 31, 2020.",
|
55 |
+
"hint": "Use a WHERE clause with membership_date.",
|
56 |
+
"expected_sql": "SELECT name, email FROM users WHERE membership_date > '2020-12-31';"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Books by George Orwell",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve all books written by George Orwell.",
|
63 |
+
"hint": "Use a WHERE clause to filter by author.",
|
64 |
+
"expected_sql": "SELECT title, genre FROM books WHERE author = 'George Orwell';"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List active loans",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show all loans that have not been returned.",
|
71 |
+
"hint": "Use a WHERE clause to check for NULL return_date.",
|
72 |
+
"expected_sql": "SELECT loan_id, book_id, user_id, issue_date FROM loans WHERE return_date IS NULL;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "List books by publication year",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Show all book titles and their publication years, ordered by year ascending.",
|
79 |
+
"hint": "Use ORDER BY clause on year.",
|
80 |
+
"expected_sql": "SELECT title, year FROM books ORDER BY year ASC;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Count loans per book",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Show the number of loans for each book, including books with zero loans.",
|
87 |
+
"hint": "Use a LEFT JOIN and GROUP BY book title.",
|
88 |
+
"expected_sql": "SELECT b.title, COUNT(l.loan_id) AS loan_count FROM books b LEFT JOIN loans l ON b.book_id = l.book_id GROUP BY b.title;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Total loans per user",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Calculate the total number of loans for each user, including users with zero loans.",
|
95 |
+
"hint": "Use a LEFT JOIN and GROUP BY user name.",
|
96 |
+
"expected_sql": "SELECT u.name, COUNT(l.loan_id) AS total_loans FROM users u LEFT JOIN loans l ON u.user_id = l.user_id GROUP BY u.name;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Books by genre count",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Count the number of books in each genre.",
|
103 |
+
"hint": "Use GROUP BY on genre and COUNT.",
|
104 |
+
"expected_sql": "SELECT genre, COUNT(book_id) AS book_count FROM books GROUP BY genre;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Active loans by genre",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Show the number of active loans (not returned) for each book genre.",
|
111 |
+
"hint": "Join books and loans, filter by NULL return_date, and GROUP BY genre.",
|
112 |
+
"expected_sql": "SELECT b.genre, COUNT(l.loan_id) AS active_loans FROM books b JOIN loans l ON b.book_id = l.book_id WHERE l.return_date IS NULL GROUP BY b.genre;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Users with multiple loans",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "Find users who have taken out more than one loan.",
|
119 |
+
"hint": "Use GROUP BY on user name and HAVING clause.",
|
120 |
+
"expected_sql": "SELECT u.name, COUNT(l.loan_id) AS loan_count FROM users u JOIN loans l ON u.user_id = l.user_id GROUP BY u.name HAVING COUNT(l.loan_id) > 1;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Books never loaned",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List books that have never been loaned.",
|
127 |
+
"hint": "Use a LEFT JOIN and check for NULL in the loans table.",
|
128 |
+
"expected_sql": "SELECT b.title FROM books b LEFT JOIN loans l ON b.book_id = l.book_id WHERE l.loan_id IS NULL;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Loan details with book and user",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show loan details including book title, user name, and issue date for all loans.",
|
135 |
+
"hint": "Join books, users, and loans tables.",
|
136 |
+
"expected_sql": "SELECT l.loan_id, b.title, u.name, l.issue_date FROM loans l JOIN books b ON l.book_id = b.book_id JOIN users u ON l.user_id = u.user_id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Books loaned in 2022",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "List all books that were loaned in 2022.",
|
143 |
+
"hint": "Use a WHERE clause with LIKE on issue_date and JOIN with books.",
|
144 |
+
"expected_sql": "SELECT DISTINCT b.title FROM books b JOIN loans l ON b.book_id = l.book_id WHERE l.issue_date LIKE '2022%';"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Average loan duration for returned books",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "Calculate the average number of days for loans that have been returned.",
|
151 |
+
"hint": "Use JULIANDAY to calculate date difference and AVG.",
|
152 |
+
"expected_sql": "SELECT AVG(JULIANDAY(return_date) - JULIANDAY(issue_date)) AS avg_loan_days FROM loans WHERE return_date IS NOT NULL;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Most popular author by loans",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Find the author with the most loans.",
|
159 |
+
"hint": "Join books and loans, GROUP BY author, and use LIMIT.",
|
160 |
+
"expected_sql": "SELECT b.author, COUNT(l.loan_id) AS loan_count FROM books b JOIN loans l ON b.book_id = l.book_id GROUP BY b.author ORDER BY loan_count DESC LIMIT 1;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Overdue loans with user details",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Show user names, book titles, and days overdue for loans not returned by '2023-03-01'.",
|
167 |
+
"hint": "Join tables, filter for overdue loans, and calculate days overdue.",
|
168 |
+
"expected_sql": "SELECT u.name, b.title, ROUND(JULIANDAY('2023-03-01') - JULIANDAY(l.due_date), 1) AS days_overdue FROM loans l JOIN books b ON l.book_id = b.book_id JOIN users u ON l.user_id = u.user_id WHERE l.due_date < '2023-03-01' AND l.return_date IS NULL;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Most active user by loan duration",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Find the user with the highest total loan duration for returned books.",
|
175 |
+
"hint": "Join users and loans, calculate duration, GROUP BY user, and use LIMIT.",
|
176 |
+
"expected_sql": "SELECT u.name, SUM(JULIANDAY(l.return_date) - JULIANDAY(l.issue_date)) AS total_loan_days FROM users u JOIN loans l ON u.user_id = l.user_id WHERE l.return_date IS NOT NULL GROUP BY u.name ORDER BY total_loan_days DESC LIMIT 1;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Books with high demand",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Identify books with more loans than their available copies.",
|
183 |
+
"hint": "Join books and loans, GROUP BY book, and use HAVING clause.",
|
184 |
+
"expected_sql": "SELECT b.title, COUNT(l.loan_id) AS loan_count, b.available_copies FROM books b JOIN loans l ON b.book_id = l.book_id GROUP BY b.title, b.available_copies HAVING COUNT(l.loan_id) > b.available_copies;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Longest overdue loan",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "Find the loan with the longest overdue period as of '2023-03-01'.",
|
191 |
+
"hint": "Calculate days overdue, filter for overdue loans, and use LIMIT.",
|
192 |
+
"expected_sql": "SELECT l.loan_id, b.title, ROUND(JULIANDAY('2023-03-01') - JULIANDAY(l.due_date), 1) AS days_overdue FROM loans l JOIN books b ON l.book_id = b.book_id WHERE l.return_date IS NULL AND l.due_date < '2023-03-01' ORDER BY days_overdue DESC LIMIT 1;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Loan activity by month",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the number of loans issued per month in 2022 and 2023.",
|
199 |
+
"hint": "Use STRFTIME to extract the month and GROUP BY.",
|
200 |
+
"expected_sql": "SELECT STRFTIME('%Y-%m', issue_date) AS month, COUNT(loan_id) AS loan_count FROM loans GROUP BY month;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Users with no returns",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "List users who have loans but no returned books.",
|
207 |
+
"hint": "Use JOIN and check for NULL return_date with HAVING clause.",
|
208 |
+
"expected_sql": "SELECT u.name FROM users u JOIN loans l ON u.user_id = l.user_id GROUP BY u.name HAVING COUNT(l.return_date) = 0;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Books loaned by multiple users",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Find books that have been loaned by more than one distinct user.",
|
215 |
+
"hint": "Use COUNT and DISTINCT on user_id, GROUP BY book title.",
|
216 |
+
"expected_sql": "SELECT b.title, COUNT(DISTINCT l.user_id) AS user_count FROM books b JOIN loans l ON b.book_id = l.book_id GROUP BY b.title HAVING COUNT(DISTINCT l.user_id) > 1;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Average loan duration by genre",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Calculate the average loan duration for returned books by genre.",
|
223 |
+
"hint": "Join books and loans, calculate duration, and GROUP BY genre.",
|
224 |
+
"expected_sql": "SELECT b.genre, AVG(JULIANDAY(l.return_date) - JULIANDAY(l.issue_date)) AS avg_loan_days FROM books b JOIN loans l ON b.book_id = l.book_id WHERE l.return_date IS NOT NULL GROUP BY b.genre;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Users borrowing all science fiction books",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "List users who have borrowed every book in the Science Fiction genre.",
|
231 |
+
"hint": "Count distinct Science Fiction books per user and compare with total Science Fiction books.",
|
232 |
+
"expected_sql": "SELECT u.name FROM users u JOIN loans l ON u.user_id = l.user_id JOIN books b ON l.book_id = b.book_id WHERE b.genre = 'Science Fiction' GROUP BY u.name HAVING COUNT(DISTINCT b.book_id) = (SELECT COUNT(*) FROM books WHERE genre = 'Science Fiction');"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Earliest and latest loan per book",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Show the earliest and latest issue dates for each book that has been loaned.",
|
239 |
+
"hint": "Use MIN and MAX on issue_date, GROUP BY book title.",
|
240 |
+
"expected_sql": "SELECT b.title, MIN(l.issue_date) AS earliest_loan, MAX(l.issue_date) AS latest_loan FROM books b JOIN loans l ON b.book_id = l.book_id GROUP BY b.title;"
|
241 |
+
}
|
242 |
+
]
|
app/questions/restaurant.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List all main course items",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and prices of all menu items in the Main Course category.",
|
7 |
+
"hint": "Use a WHERE clause to filter by category.",
|
8 |
+
"expected_sql": "SELECT name, price FROM menu_items WHERE category = 'Main Course';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find orders by customer ID 1",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show all orders placed by the customer with ID 1.",
|
15 |
+
"hint": "Use a WHERE clause to filter by customer_id.",
|
16 |
+
"expected_sql": "SELECT order_id, order_date, total_amount FROM orders WHERE customer_id = 1;"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List all beverages",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve the names and prices of all items in the Beverage category.",
|
23 |
+
"hint": "Use a WHERE clause to filter by category.",
|
24 |
+
"expected_sql": "SELECT name, price FROM menu_items WHERE category = 'Beverage';"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Find orders on June 11, 2023",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show all orders placed on June 11, 2023.",
|
31 |
+
"hint": "Use a WHERE clause with date comparison.",
|
32 |
+
"expected_sql": "SELECT order_id, customer_id, total_amount FROM orders WHERE order_date = '2023-06-11';"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique customer emails",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique customer email addresses.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate emails.",
|
40 |
+
"expected_sql": "SELECT DISTINCT email FROM customers;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Items in order ID 3",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Show the menu items and quantities for order ID 3.",
|
47 |
+
"hint": "Join order_items with menu_items and filter by order_id.",
|
48 |
+
"expected_sql": "SELECT m.name, oi.quantity FROM order_items oi JOIN menu_items m ON oi.item_id = m.item_id WHERE oi.order_id = 3;"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List customer contact details",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Show names and contact numbers of all customers.",
|
55 |
+
"hint": "Select name and contact from the customers table.",
|
56 |
+
"expected_sql": "SELECT name, contact FROM customers;"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Find high-priced items",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Retrieve menu items with a price greater than 150.",
|
63 |
+
"hint": "Use a WHERE clause to filter by price.",
|
64 |
+
"expected_sql": "SELECT name, price FROM menu_items WHERE price > 150;"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List all order items",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show all order items with their quantities.",
|
71 |
+
"hint": "Select from the order_items table.",
|
72 |
+
"expected_sql": "SELECT order_item_id, order_id, item_id, quantity FROM order_items;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "Find orders above 300",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Show orders with a total amount greater than 300.",
|
79 |
+
"hint": "Use a WHERE clause to filter by total_amount.",
|
80 |
+
"expected_sql": "SELECT order_id, customer_id, total_amount FROM orders WHERE total_amount > 300;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Count orders per customer",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Show the number of orders placed by each customer, including those with zero orders.",
|
87 |
+
"hint": "Use a LEFT JOIN and GROUP BY customer name.",
|
88 |
+
"expected_sql": "SELECT c.name, COUNT(o.order_id) AS order_count FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.name;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Total items ordered per category",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Calculate the total quantity of items ordered for each menu item category.",
|
95 |
+
"hint": "Join menu_items and order_items, then GROUP BY category.",
|
96 |
+
"expected_sql": "SELECT m.category, SUM(oi.quantity) AS total_quantity FROM menu_items m JOIN order_items oi ON m.item_id = oi.item_id GROUP BY m.category;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Average order amount",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Calculate the average total amount of all orders.",
|
103 |
+
"hint": "Use AVG on total_amount in the orders table.",
|
104 |
+
"expected_sql": "SELECT AVG(total_amount) AS avg_order_amount FROM orders;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Most ordered menu item",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Identify the menu item with the highest total quantity ordered.",
|
111 |
+
"hint": "Join order_items and menu_items, GROUP BY item name, and use LIMIT.",
|
112 |
+
"expected_sql": "SELECT m.name, SUM(oi.quantity) AS total_quantity FROM menu_items m JOIN order_items oi ON m.item_id = oi.item_id GROUP BY m.name ORDER BY total_quantity DESC LIMIT 1;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Customers with multiple orders",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "Find customers who have placed more than one order.",
|
119 |
+
"hint": "Use GROUP BY on customer name and HAVING clause.",
|
120 |
+
"expected_sql": "SELECT c.name, COUNT(o.order_id) AS order_count FROM customers c JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.name HAVING COUNT(o.order_id) > 1;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Menu items never ordered",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "List menu items that have never been ordered.",
|
127 |
+
"hint": "Use a LEFT JOIN and check for NULL in the order_items table.",
|
128 |
+
"expected_sql": "SELECT m.name FROM menu_items m LEFT JOIN order_items oi ON m.item_id = oi.item_id WHERE oi.order_item_id IS NULL;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Order details with items",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show order details including customer name and menu items for each order.",
|
135 |
+
"hint": "Join orders, customers, order_items, and menu_items tables.",
|
136 |
+
"expected_sql": "SELECT o.order_id, c.name AS customer_name, m.name AS item_name, oi.quantity FROM orders o JOIN customers c ON o.customer_id = c.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Total revenue per customer",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "Calculate the total amount spent by each customer based on order totals.",
|
143 |
+
"hint": "Use GROUP BY on customer name and SUM on total_amount.",
|
144 |
+
"expected_sql": "SELECT c.name, SUM(o.total_amount) AS total_spent FROM customers c JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.name;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Orders with multiple items",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "List orders that include more than one type of menu item.",
|
151 |
+
"hint": "Use GROUP BY on order_id and HAVING clause on COUNT of item_id.",
|
152 |
+
"expected_sql": "SELECT o.order_id, COUNT(oi.item_id) AS item_count FROM orders o JOIN order_items oi ON o.order_id = oi.order_id GROUP BY o.order_id HAVING COUNT(oi.item_id) > 1;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Items ordered with high quantity",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Show menu items with a total ordered quantity greater than 2.",
|
159 |
+
"hint": "Join order_items and menu_items, GROUP BY item name, and use HAVING.",
|
160 |
+
"expected_sql": "SELECT m.name, SUM(oi.quantity) AS total_quantity FROM menu_items m JOIN order_items oi ON m.item_id = oi.item_id GROUP BY m.name HAVING SUM(oi.quantity) > 2;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Customer order value analysis",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Calculate the total value of orders (sum of price * quantity) for each customer.",
|
167 |
+
"hint": "Join all tables, multiply price by quantity, and GROUP BY customer name.",
|
168 |
+
"expected_sql": "SELECT c.name, SUM(m.price * oi.quantity) AS total_order_value FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id GROUP BY c.name;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Most expensive order",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Identify the order with the highest calculated total based on item prices and quantities.",
|
175 |
+
"hint": "Join orders, order_items, and menu_items, calculate total, and use LIMIT.",
|
176 |
+
"expected_sql": "SELECT o.order_id, SUM(m.price * oi.quantity) AS calculated_total FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id GROUP BY o.order_id ORDER BY calculated_total DESC LIMIT 1;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Customers ordering all main courses",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Find customers who have ordered every item in the Main Course category.",
|
183 |
+
"hint": "Count distinct Main Course items per customer and compare with total Main Course items.",
|
184 |
+
"expected_sql": "SELECT c.name FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id WHERE m.category = 'Main Course' GROUP BY c.name HAVING COUNT(DISTINCT m.item_id) = (SELECT COUNT(*) FROM menu_items WHERE category = 'Main Course');"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Average item price per order",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "Calculate the average price of items in each order.",
|
191 |
+
"hint": "Join order_items and menu_items, GROUP BY order_id, and use AVG.",
|
192 |
+
"expected_sql": "SELECT o.order_id, AVG(m.price) AS avg_item_price FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id GROUP BY o.order_id;"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Order frequency by date",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the number of orders placed on each date in June 2023.",
|
199 |
+
"hint": "Use GROUP BY on order_date and COUNT.",
|
200 |
+
"expected_sql": "SELECT order_date, COUNT(order_id) AS order_count FROM orders WHERE order_date LIKE '2023-06%' GROUP BY order_date;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Customers who ordered Margherita Pizza",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "List customers who have ordered a Margherita Pizza.",
|
207 |
+
"hint": "Join all tables and filter by item name.",
|
208 |
+
"expected_sql": "SELECT DISTINCT c.name FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id WHERE m.name = 'Margherita Pizza';"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Category revenue contribution",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Calculate the total revenue generated by each menu item category.",
|
215 |
+
"hint": "Join menu_items and order_items, multiply price by quantity, and GROUP BY category.",
|
216 |
+
"expected_sql": "SELECT m.category, SUM(m.price * oi.quantity) AS total_revenue FROM menu_items m JOIN order_items oi ON m.item_id = oi.item_id GROUP BY m.category;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Orders with high item diversity",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Find orders that include items from more than one category.",
|
223 |
+
"hint": "Join tables, GROUP BY order_id, and COUNT distinct categories.",
|
224 |
+
"expected_sql": "SELECT o.order_id, COUNT(DISTINCT m.category) AS category_count FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN menu_items m ON oi.item_id = m.item_id GROUP BY o.order_id HAVING COUNT(DISTINCT m.category) > 1;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Customer spending rank",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "Rank customers by their total spending (sum of order totals).",
|
231 |
+
"hint": "Use GROUP BY on customer name, SUM, and ORDER BY.",
|
232 |
+
"expected_sql": "SELECT c.name, SUM(o.total_amount) AS total_spent FROM customers c JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.name ORDER BY total_spent DESC;"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Items with high revenue contribution",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Identify menu items contributing more than 200 in total revenue (price * quantity).",
|
239 |
+
"hint": "Join menu_items and order_items, calculate revenue, and use HAVING.",
|
240 |
+
"expected_sql": "SELECT m.name, SUM(m.price * oi.quantity) AS total_revenue FROM menu_items m JOIN order_items oi ON m.item_id = oi.item_id GROUP BY m.name HAVING SUM(m.price * oi.quantity) > 200;"
|
241 |
+
}
|
242 |
+
]
|
app/questions/retail.json
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"id": "q1",
|
4 |
+
"title": "List electronics products",
|
5 |
+
"difficulty": "Beginner",
|
6 |
+
"description": "Retrieve the names and prices of all products in the Electronics category.",
|
7 |
+
"hint": "Use a WHERE clause to filter by category.",
|
8 |
+
"expected_sql": "SELECT product_name, price FROM inventory WHERE category = 'Electronics';"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "q2",
|
12 |
+
"title": "Find low stock products",
|
13 |
+
"difficulty": "Beginner",
|
14 |
+
"description": "Show products with stock below their reorder level.",
|
15 |
+
"hint": "Use a WHERE clause to compare stock and reorder_level.",
|
16 |
+
"expected_sql": "SELECT product_name, stock, reorder_level FROM inventory WHERE stock < reorder_level;"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"id": "q3",
|
20 |
+
"title": "List restocks for product ID 1",
|
21 |
+
"difficulty": "Beginner",
|
22 |
+
"description": "Retrieve all restock details for the product with ID 1.",
|
23 |
+
"hint": "Use a WHERE clause to filter by product_id in the restocks table.",
|
24 |
+
"expected_sql": "SELECT restock_id, restock_date, quantity, supplier FROM restocks WHERE product_id = 1;"
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"id": "q4",
|
28 |
+
"title": "Find products by price range",
|
29 |
+
"difficulty": "Beginner",
|
30 |
+
"description": "Show products with prices between 100 and 500, inclusive.",
|
31 |
+
"hint": "Use a BETWEEN clause for the price range.",
|
32 |
+
"expected_sql": "SELECT product_name, price FROM inventory WHERE price BETWEEN 100 AND 500;"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"id": "q5",
|
36 |
+
"title": "List unique suppliers",
|
37 |
+
"difficulty": "Beginner",
|
38 |
+
"description": "Retrieve all unique suppliers from the restocks table.",
|
39 |
+
"hint": "Use DISTINCT to avoid duplicate suppliers.",
|
40 |
+
"expected_sql": "SELECT DISTINCT supplier FROM restocks;"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"id": "q6",
|
44 |
+
"title": "Find restocks in 2023",
|
45 |
+
"difficulty": "Beginner",
|
46 |
+
"description": "Show all restocks that occurred in 2023.",
|
47 |
+
"hint": "Use a WHERE clause with LIKE for restock_date.",
|
48 |
+
"expected_sql": "SELECT restock_id, product_id, restock_date, quantity FROM restocks WHERE restock_date LIKE '2023%';"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"id": "q7",
|
52 |
+
"title": "List accessories products",
|
53 |
+
"difficulty": "Beginner",
|
54 |
+
"description": "Retrieve product names and stock for all items in the Accessories category.",
|
55 |
+
"hint": "Use a WHERE clause to filter by category.",
|
56 |
+
"expected_sql": "SELECT product_name, stock FROM inventory WHERE category = 'Accessories';"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"id": "q8",
|
60 |
+
"title": "Find high stock products",
|
61 |
+
"difficulty": "Beginner",
|
62 |
+
"description": "Show products with stock greater than 20.",
|
63 |
+
"hint": "Use a WHERE clause to filter by stock.",
|
64 |
+
"expected_sql": "SELECT product_name, stock FROM inventory WHERE stock > 20;"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "q9",
|
68 |
+
"title": "List restock quantities",
|
69 |
+
"difficulty": "Beginner",
|
70 |
+
"description": "Show the quantity restocked for each restock event.",
|
71 |
+
"hint": "Select restock_id and quantity from the restocks table.",
|
72 |
+
"expected_sql": "SELECT restock_id, quantity FROM restocks;"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"id": "q10",
|
76 |
+
"title": "Find products with high reorder level",
|
77 |
+
"difficulty": "Beginner",
|
78 |
+
"description": "Retrieve products with a reorder level greater than 5.",
|
79 |
+
"hint": "Use a WHERE clause to filter by reorder_level.",
|
80 |
+
"expected_sql": "SELECT product_name, reorder_level FROM inventory WHERE reorder_level > 5;"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"id": "q11",
|
84 |
+
"title": "Total stock per category",
|
85 |
+
"difficulty": "Intermediate",
|
86 |
+
"description": "Calculate the total stock for each product category.",
|
87 |
+
"hint": "Use GROUP BY on category and SUM on stock.",
|
88 |
+
"expected_sql": "SELECT category, SUM(stock) AS total_stock FROM inventory GROUP BY category;"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"id": "q12",
|
92 |
+
"title": "Average price per category",
|
93 |
+
"difficulty": "Intermediate",
|
94 |
+
"description": "Calculate the average price of products in each category.",
|
95 |
+
"hint": "Use GROUP BY on category and AVG on price.",
|
96 |
+
"expected_sql": "SELECT category, AVG(price) AS avg_price FROM inventory GROUP BY category;"
|
97 |
+
},
|
98 |
+
{
|
99 |
+
"id": "q13",
|
100 |
+
"title": "Count restocks per supplier",
|
101 |
+
"difficulty": "Intermediate",
|
102 |
+
"description": "Show the number of restock events for each supplier.",
|
103 |
+
"hint": "Use GROUP BY on supplier and COUNT.",
|
104 |
+
"expected_sql": "SELECT supplier, COUNT(restock_id) AS restock_count FROM restocks GROUP BY supplier;"
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": "q14",
|
108 |
+
"title": "Total restock quantity per product",
|
109 |
+
"difficulty": "Intermediate",
|
110 |
+
"description": "Calculate the total quantity restocked for each product.",
|
111 |
+
"hint": "Join inventory and restocks, then GROUP BY product_name.",
|
112 |
+
"expected_sql": "SELECT i.product_name, SUM(r.quantity) AS total_restocked FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
113 |
+
},
|
114 |
+
{
|
115 |
+
"id": "q15",
|
116 |
+
"title": "Products with multiple restocks",
|
117 |
+
"difficulty": "Intermediate",
|
118 |
+
"description": "List products that have been restocked more than once.",
|
119 |
+
"hint": "Use GROUP BY on product_name and HAVING clause.",
|
120 |
+
"expected_sql": "SELECT i.product_name, COUNT(r.restock_id) AS restock_count FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name HAVING COUNT(r.restock_id) > 1;"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"id": "q16",
|
124 |
+
"title": "Total inventory value",
|
125 |
+
"difficulty": "Intermediate",
|
126 |
+
"description": "Calculate the total value of inventory (price * stock) for each category.",
|
127 |
+
"hint": "Use GROUP BY on category and SUM on price * stock.",
|
128 |
+
"expected_sql": "SELECT category, SUM(price * stock) AS total_value FROM inventory GROUP BY category;"
|
129 |
+
},
|
130 |
+
{
|
131 |
+
"id": "q17",
|
132 |
+
"title": "Restock details with product names",
|
133 |
+
"difficulty": "Intermediate",
|
134 |
+
"description": "Show restock details including product names, quantities, and suppliers.",
|
135 |
+
"hint": "Join inventory and restocks tables.",
|
136 |
+
"expected_sql": "SELECT r.restock_id, i.product_name, r.restock_date, r.quantity, r.supplier FROM inventory i JOIN restocks r ON i.product_id = r.product_id;"
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"id": "q18",
|
140 |
+
"title": "Products needing restock",
|
141 |
+
"difficulty": "Intermediate",
|
142 |
+
"description": "List products with stock at or below their reorder level, ordered by stock ascending.",
|
143 |
+
"hint": "Use WHERE and ORDER BY clauses.",
|
144 |
+
"expected_sql": "SELECT product_name, stock, reorder_level FROM inventory WHERE stock <= reorder_level ORDER BY stock ASC;"
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"id": "q19",
|
148 |
+
"title": "Suppliers with large restocks",
|
149 |
+
"difficulty": "Intermediate",
|
150 |
+
"description": "Find suppliers who have restocked quantities greater than 20 in a single restock event.",
|
151 |
+
"hint": "Use WHERE clause on quantity and select DISTINCT supplier.",
|
152 |
+
"expected_sql": "SELECT DISTINCT supplier FROM restocks WHERE quantity > 20;"
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"id": "q20",
|
156 |
+
"title": "Restock frequency by product",
|
157 |
+
"difficulty": "Intermediate",
|
158 |
+
"description": "Show the number of restock events for each product, including those with zero restocks.",
|
159 |
+
"hint": "Use a LEFT JOIN and GROUP BY product_name.",
|
160 |
+
"expected_sql": "SELECT i.product_name, COUNT(r.restock_id) AS restock_count FROM inventory i LEFT JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"id": "q21",
|
164 |
+
"title": "Most expensive restocked product",
|
165 |
+
"difficulty": "Advanced",
|
166 |
+
"description": "Find the product with the highest price that has been restocked.",
|
167 |
+
"hint": "Join inventory and restocks, use ORDER BY and LIMIT.",
|
168 |
+
"expected_sql": "SELECT i.product_name, i.price FROM inventory i JOIN restocks r ON i.product_id = r.product_id ORDER BY i.price DESC LIMIT 1;"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"id": "q22",
|
172 |
+
"title": "Total restock value per supplier",
|
173 |
+
"difficulty": "Advanced",
|
174 |
+
"description": "Calculate the total value of restocks (price * quantity) for each supplier.",
|
175 |
+
"hint": "Join tables, multiply price by quantity, and GROUP BY supplier.",
|
176 |
+
"expected_sql": "SELECT r.supplier, SUM(i.price * r.quantity) AS total_restock_value FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY r.supplier;"
|
177 |
+
},
|
178 |
+
{
|
179 |
+
"id": "q23",
|
180 |
+
"title": "Latest restock per product",
|
181 |
+
"difficulty": "Advanced",
|
182 |
+
"description": "Show the most recent restock date for each product that has been restocked.",
|
183 |
+
"hint": "Use GROUP BY on product_name and MAX on restock_date.",
|
184 |
+
"expected_sql": "SELECT i.product_name, MAX(r.restock_date) AS latest_restock FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name;"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "q24",
|
188 |
+
"title": "Suppliers restocking all electronics",
|
189 |
+
"difficulty": "Advanced",
|
190 |
+
"description": "List suppliers who have restocked every product in the Electronics category.",
|
191 |
+
"hint": "Count distinct Electronics products per supplier and compare with total Electronics products.",
|
192 |
+
"expected_sql": "SELECT r.supplier FROM restocks r JOIN inventory i ON r.product_id = i.product_id WHERE i.category = 'Electronics' GROUP BY r.supplier HAVING COUNT(DISTINCT r.product_id) = (SELECT COUNT(*) FROM inventory WHERE category = 'Electronics');"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "q25",
|
196 |
+
"title": "Restock trend by month",
|
197 |
+
"difficulty": "Advanced",
|
198 |
+
"description": "Show the total quantity restocked per month in 2023.",
|
199 |
+
"hint": "Use STRFTIME to extract the month and GROUP BY.",
|
200 |
+
"expected_sql": "SELECT STRFTIME('%Y-%m', restock_date) AS month, SUM(quantity) AS total_quantity FROM restocks WHERE restock_date LIKE '2023%' GROUP BY month;"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"id": "q26",
|
204 |
+
"title": "Products with high restock value",
|
205 |
+
"difficulty": "Advanced",
|
206 |
+
"description": "Find products where the total restock value (price * quantity) exceeds 5000.",
|
207 |
+
"hint": "Join tables, calculate value, and use HAVING clause.",
|
208 |
+
"expected_sql": "SELECT i.product_name, SUM(i.price * r.quantity) AS total_restock_value FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name HAVING SUM(i.price * r.quantity) > 5000;"
|
209 |
+
},
|
210 |
+
{
|
211 |
+
"id": "q27",
|
212 |
+
"title": "Supplier product diversity",
|
213 |
+
"difficulty": "Advanced",
|
214 |
+
"description": "Count the number of unique products each supplier has restocked.",
|
215 |
+
"hint": "Use COUNT and DISTINCT on product_id, GROUP BY supplier.",
|
216 |
+
"expected_sql": "SELECT r.supplier, COUNT(DISTINCT r.product_id) AS unique_products FROM restocks r GROUP BY r.supplier;"
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"id": "q28",
|
220 |
+
"title": "Overstocked products",
|
221 |
+
"difficulty": "Advanced",
|
222 |
+
"description": "Identify products where the total restocked quantity exceeds the current stock by more than 20.",
|
223 |
+
"hint": "Join tables, compare SUM(quantity) with stock, and use HAVING.",
|
224 |
+
"expected_sql": "SELECT i.product_name, i.stock, SUM(r.quantity) AS total_restocked FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.product_name, i.stock HAVING SUM(r.quantity) > i.stock + 20;"
|
225 |
+
},
|
226 |
+
{
|
227 |
+
"id": "q29",
|
228 |
+
"title": "Restock value by category",
|
229 |
+
"difficulty": "Advanced",
|
230 |
+
"description": "Calculate the total restock value (price * quantity) for each product category.",
|
231 |
+
"hint": "Join tables, multiply price by quantity, and GROUP BY category.",
|
232 |
+
"expected_sql": "SELECT i.category, SUM(i.price * r.quantity) AS total_restock_value FROM inventory i JOIN restocks r ON i.product_id = r.product_id GROUP BY i.category;"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "q30",
|
236 |
+
"title": "Earliest and latest restock per supplier",
|
237 |
+
"difficulty": "Advanced",
|
238 |
+
"description": "Show the earliest and latest restock dates for each supplier.",
|
239 |
+
"hint": "Use MIN and MAX on restock_date, GROUP BY supplier.",
|
240 |
+
"expected_sql": "SELECT supplier, MIN(restock_date) AS earliest_restock, MAX(restock_date) AS latest_restock FROM restocks GROUP BY supplier;"
|
241 |
+
}
|
242 |
+
]
|
app/schemas.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
|
3 |
+
class RunQueryRequest(BaseModel):
|
4 |
+
query: str
|
5 |
+
|
6 |
+
class ValidateQueryRequest(BaseModel):
|
7 |
+
user_query: str
|
8 |
+
expected_query: str
|
app/schemas/banking.sql
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Customers table
|
2 |
+
CREATE TABLE customers (
|
3 |
+
customer_id INTEGER PRIMARY KEY,
|
4 |
+
name TEXT NOT NULL,
|
5 |
+
age INTEGER,
|
6 |
+
city TEXT,
|
7 |
+
email TEXT
|
8 |
+
);
|
9 |
+
|
10 |
+
-- Accounts table
|
11 |
+
CREATE TABLE accounts (
|
12 |
+
account_id INTEGER PRIMARY KEY,
|
13 |
+
customer_id INTEGER,
|
14 |
+
account_type TEXT CHECK(account_type IN ('Savings', 'Current', 'Loan')),
|
15 |
+
balance REAL,
|
16 |
+
opened_on DATE,
|
17 |
+
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
|
18 |
+
);
|
19 |
+
|
20 |
+
-- Sample data: customers
|
21 |
+
INSERT INTO customers (customer_id, name, age, city, email) VALUES (1, 'John Doe', 30, 'New York', 'john.doe@example.com');
|
22 |
+
INSERT INTO customers (customer_id, name, age, city, email) VALUES (2, 'Jane Smith', 25, 'Los Angeles', 'jane.smith@example.com');
|
23 |
+
INSERT INTO customers (customer_id, name, age, city, email) VALUES (3, 'Michael Johnson', 45, 'Chicago', 'michael.j@example.com');
|
24 |
+
INSERT INTO customers (customer_id, name, age, city, email) VALUES (4, 'Emily Davis', 28, 'Houston', 'emily.davis@example.com');
|
25 |
+
INSERT INTO customers (customer_id, name, age, city, email) VALUES (5, 'David Wilson', 35, 'Phoenix', 'david.w@example.com');
|
26 |
+
|
27 |
+
-- Sample data: accounts
|
28 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (1, 1, 'Savings', 1000.50, '2023-01-15');
|
29 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (2, 2, 'Savings', 500.75, '2022-11-20');
|
30 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (3, 3, 'Current', 2500.00, '2023-03-10');
|
31 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (4, 4, 'Loan', -10000.00, '2021-06-01');
|
32 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (5, 5, 'Savings', 350.00, '2024-04-25');
|
33 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (6, 1, 'Loan', -5000.00, '2022-05-10');
|
34 |
+
INSERT INTO accounts (account_id, customer_id, account_type, balance, opened_on) VALUES (7, 2, 'Current', 700.00, '2023-08-12');
|
app/schemas/college.sql
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Students table
|
2 |
+
CREATE TABLE students (
|
3 |
+
student_id INTEGER PRIMARY KEY,
|
4 |
+
name TEXT NOT NULL,
|
5 |
+
department TEXT,
|
6 |
+
year INTEGER,
|
7 |
+
email TEXT
|
8 |
+
);
|
9 |
+
|
10 |
+
-- Courses table
|
11 |
+
CREATE TABLE courses (
|
12 |
+
course_id INTEGER PRIMARY KEY,
|
13 |
+
name TEXT NOT NULL,
|
14 |
+
department TEXT,
|
15 |
+
credits INTEGER,
|
16 |
+
instructor TEXT
|
17 |
+
);
|
18 |
+
|
19 |
+
-- Enrollments table
|
20 |
+
CREATE TABLE enrollments (
|
21 |
+
enrollment_id INTEGER PRIMARY KEY,
|
22 |
+
student_id INTEGER,
|
23 |
+
course_id INTEGER,
|
24 |
+
semester TEXT,
|
25 |
+
grade TEXT,
|
26 |
+
FOREIGN KEY (student_id) REFERENCES students(student_id),
|
27 |
+
FOREIGN KEY (course_id) REFERENCES courses(course_id)
|
28 |
+
);
|
29 |
+
|
30 |
+
|
31 |
+
INSERT INTO students (student_id, name, department, year, email) VALUES
|
32 |
+
(1, 'Rahul Sharma', 'CSE', 2, 'rahul.s@univ.edu'),
|
33 |
+
(2, 'Anjali Mehta', 'ECE', 3, 'anjali.m@univ.edu'),
|
34 |
+
(3, 'Soham Verma', 'ME', 2, 'soham.v@univ.edu'),
|
35 |
+
(4, 'Pooja Singh', 'CSE', 1, 'pooja.s@univ.edu'),
|
36 |
+
(5, 'Karan Patel', 'EEE', 4, 'karan.p@univ.edu');
|
37 |
+
|
38 |
+
INSERT INTO courses (course_id, name, department, credits, instructor) VALUES
|
39 |
+
(101, 'Data Structures', 'CSE', 4, 'Dr. Anil Kapoor'),
|
40 |
+
(102, 'Digital Electronics', 'ECE', 3, 'Dr. Priya Nair'),
|
41 |
+
(103, 'Thermodynamics', 'ME', 4, 'Dr. Ramesh Rao'),
|
42 |
+
(104, 'Operating Systems', 'CSE', 4, 'Dr. Neha Malhotra'),
|
43 |
+
(105, 'Power Systems', 'EEE', 3, 'Dr. Amit Kumar');
|
44 |
+
|
45 |
+
INSERT INTO enrollments (enrollment_id, student_id, course_id, semester, grade) VALUES
|
46 |
+
(1, 1, 101, 'Spring 2023', 'A'),
|
47 |
+
(2, 1, 104, 'Spring 2023', 'B+'),
|
48 |
+
(3, 2, 102, 'Spring 2023', 'A-'),
|
49 |
+
(4, 3, 103, 'Spring 2023', 'B'),
|
50 |
+
(5, 4, 101, 'Spring 2023', 'A'),
|
51 |
+
(6, 4, 104, 'Spring 2023', 'A-'),
|
52 |
+
(7, 5, 105, 'Spring 2023', 'B+');
|
app/schemas/e_commerce.sql
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Sales Table
|
2 |
+
CREATE TABLE sales (
|
3 |
+
id INTEGER PRIMARY KEY,
|
4 |
+
product_name TEXT NOT NULL,
|
5 |
+
category TEXT,
|
6 |
+
sale_amount REAL NOT NULL,
|
7 |
+
sale_date DATE
|
8 |
+
);
|
9 |
+
|
10 |
+
-- Orders Table
|
11 |
+
CREATE TABLE orders (
|
12 |
+
order_id INTEGER PRIMARY KEY,
|
13 |
+
customer_id INTEGER,
|
14 |
+
product_id INTEGER,
|
15 |
+
quantity INTEGER DEFAULT 1,
|
16 |
+
order_date DATE,
|
17 |
+
FOREIGN KEY (product_id) REFERENCES sales(id)
|
18 |
+
);
|
19 |
+
|
20 |
+
|
21 |
+
INSERT INTO sales (id, product_name, category, sale_amount, sale_date) VALUES
|
22 |
+
(1, 'Laptop', 'Electronics', 1200.0, '2023-01-01'),
|
23 |
+
(2, 'Phone', 'Electronics', 800.0, '2023-01-02'),
|
24 |
+
(3, 'Laptop', 'Electronics', 1500.0, '2023-01-03'),
|
25 |
+
(4, 'Tablet', 'Electronics', 600.0, '2023-01-04'),
|
26 |
+
(5, 'Phone', 'Electronics', 900.0, '2023-01-05'),
|
27 |
+
(6, 'Monitor', 'Accessories', 400.0, '2023-01-06'),
|
28 |
+
(7, 'Keyboard', 'Accessories', 100.0, '2023-01-07'),
|
29 |
+
(8, 'Mouse', 'Accessories', 50.0, '2023-01-08'),
|
30 |
+
(9, 'Charger', 'Accessories', 70.0, '2023-01-09'),
|
31 |
+
(10, 'Headphones', 'Accessories', 200.0, '2023-01-10');
|
32 |
+
|
33 |
+
|
34 |
+
INSERT INTO orders (order_id, customer_id, product_id, quantity, order_date) VALUES
|
35 |
+
(1, 2001, 1, 1, '2023-01-01'),
|
36 |
+
(2, 2002, 2, 2, '2023-01-02'),
|
37 |
+
(3, 2001, 3, 1, '2023-01-03'),
|
38 |
+
(4, 2003, 4, 1, '2023-01-04'),
|
39 |
+
(5, 2001, 5, 1, '2023-01-05'),
|
40 |
+
(6, 2004, 6, 1, '2023-01-06'),
|
41 |
+
(7, 2002, 7, 3, '2023-01-07'),
|
42 |
+
(8, 2003, 8, 2, '2023-01-08'),
|
43 |
+
(9, 2005, 9, 1, '2023-01-09'),
|
44 |
+
(10, 2004, 10, 1, '2023-01-10');
|
app/schemas/hospital.sql
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Patients Table
|
2 |
+
CREATE TABLE patients (
|
3 |
+
patient_id INTEGER PRIMARY KEY,
|
4 |
+
name TEXT NOT NULL,
|
5 |
+
gender TEXT CHECK (gender IN ('Male', 'Female', 'Other')),
|
6 |
+
dob DATE,
|
7 |
+
contact TEXT
|
8 |
+
);
|
9 |
+
|
10 |
+
-- Doctors Table
|
11 |
+
CREATE TABLE doctors (
|
12 |
+
doctor_id INTEGER PRIMARY KEY,
|
13 |
+
name TEXT NOT NULL,
|
14 |
+
specialty TEXT,
|
15 |
+
contact TEXT
|
16 |
+
);
|
17 |
+
|
18 |
+
-- Appointments Table
|
19 |
+
CREATE TABLE appointments (
|
20 |
+
appointment_id INTEGER PRIMARY KEY,
|
21 |
+
patient_id INTEGER,
|
22 |
+
doctor_id INTEGER,
|
23 |
+
appointment_date DATE,
|
24 |
+
diagnosis TEXT,
|
25 |
+
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
|
26 |
+
FOREIGN KEY (doctor_id) REFERENCES doctors(doctor_id)
|
27 |
+
);
|
28 |
+
|
29 |
+
|
30 |
+
INSERT INTO patients VALUES
|
31 |
+
(1, 'Aarushi Verma', 'Female', '1995-08-20', '9999888877'),
|
32 |
+
(2, 'Rohan Mehra', 'Male', '1990-03-15', '9999123456'),
|
33 |
+
(3, 'Simran Kaur', 'Female', '1985-11-05', '8888445566'),
|
34 |
+
(4, 'Arjun Kapoor', 'Male', '2000-01-25', '7777333222'),
|
35 |
+
(5, 'Priya Das', 'Female', '1998-07-14', '9999000011');
|
36 |
+
|
37 |
+
INSERT INTO doctors VALUES
|
38 |
+
(1, 'Dr. Rakesh Sharma', 'Cardiologist', '8888777766'),
|
39 |
+
(2, 'Dr. Meena Iyer', 'Dermatologist', '8888123456'),
|
40 |
+
(3, 'Dr. Alok Mishra', 'Neurologist', '7777888899'),
|
41 |
+
(4, 'Dr. Nandini Rao', 'Pediatrician', '9999333311');
|
42 |
+
|
43 |
+
INSERT INTO appointments VALUES
|
44 |
+
(1, 1, 1, '2023-06-20', 'Routine Checkup'),
|
45 |
+
(2, 2, 2, '2023-06-25', 'Skin Rash'),
|
46 |
+
(3, 3, 1, '2023-07-02', 'Chest Pain'),
|
47 |
+
(4, 4, 3, '2023-07-10', 'Headache & Dizziness'),
|
48 |
+
(5, 5, 4, '2023-07-15', 'Child Vaccination'),
|
49 |
+
(6, 1, 3, '2023-08-01', 'Memory Loss'),
|
50 |
+
(7, 2, 1, '2023-08-05', 'High Blood Pressure');
|
app/schemas/inventory.sql
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Inventory Table
|
2 |
+
CREATE TABLE inventory (
|
3 |
+
product_id INTEGER PRIMARY KEY,
|
4 |
+
product_name TEXT NOT NULL,
|
5 |
+
category TEXT NOT NULL,
|
6 |
+
price REAL NOT NULL,
|
7 |
+
stock INTEGER NOT NULL CHECK (stock >= 0)
|
8 |
+
);
|
9 |
+
|
10 |
+
-- Restocks Table
|
11 |
+
CREATE TABLE restocks (
|
12 |
+
restock_id INTEGER PRIMARY KEY,
|
13 |
+
product_id INTEGER,
|
14 |
+
restock_date DATE NOT NULL,
|
15 |
+
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
16 |
+
supplier TEXT,
|
17 |
+
FOREIGN KEY (product_id) REFERENCES inventory(product_id)
|
18 |
+
);
|
19 |
+
|
20 |
+
|
21 |
+
INSERT INTO inventory (product_id, product_name, category, price, stock) VALUES
|
22 |
+
(1, 'Laptop', 'Electronics', 1200.00, 10),
|
23 |
+
(2, 'Smartphone', 'Electronics', 800.00, 15),
|
24 |
+
(3, 'Printer', 'Peripherals', 300.00, 5),
|
25 |
+
(4, 'Desk Chair', 'Furniture', 150.00, 20),
|
26 |
+
(5, 'Monitor', 'Peripherals', 400.00, 8),
|
27 |
+
(6, 'Mouse', 'Accessories', 40.00, 50),
|
28 |
+
(7, 'Keyboard', 'Accessories', 60.00, 30),
|
29 |
+
(8, 'Router', 'Networking', 90.00, 12),
|
30 |
+
(9, 'External HDD', 'Storage', 110.00, 7),
|
31 |
+
(10, 'USB-C Hub', 'Accessories', 25.00, 40);
|
32 |
+
|
33 |
+
INSERT INTO restocks (restock_id, product_id, restock_date, quantity, supplier) VALUES
|
34 |
+
(1, 1, '2023-01-01', 5, 'TechSupplier Inc.'),
|
35 |
+
(2, 2, '2023-01-05', 10, 'MobileMart Ltd.'),
|
36 |
+
(3, 3, '2023-01-10', 3, 'PrintPerfect'),
|
37 |
+
(4, 4, '2023-01-12', 15, 'OfficeFurnish Co.'),
|
38 |
+
(5, 5, '2023-01-15', 6, 'PeripheralWorks'),
|
39 |
+
(6, 6, '2023-01-17', 25, 'AccsGlobal'),
|
40 |
+
(7, 7, '2023-01-18', 10, 'AccsGlobal'),
|
41 |
+
(8, 8, '2023-01-20', 8, 'NetConnect'),
|
42 |
+
(9, 9, '2023-01-21', 5, 'StorageHouse'),
|
43 |
+
(10, 10, '2023-01-22', 20, 'TechSupplier Inc.'),
|
44 |
+
(11, 1, '2023-02-01', 3, 'TechSupplier Inc.'),
|
45 |
+
(12, 2, '2023-02-03', 7, 'MobileMart Ltd.');
|
46 |
+
|
app/schemas/library.sql
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Books table
|
2 |
+
CREATE TABLE books (
|
3 |
+
book_id INTEGER PRIMARY KEY,
|
4 |
+
title TEXT NOT NULL,
|
5 |
+
author TEXT NOT NULL,
|
6 |
+
genre TEXT,
|
7 |
+
year INTEGER,
|
8 |
+
available_copies INTEGER DEFAULT 1
|
9 |
+
);
|
10 |
+
|
11 |
+
-- Users table (added for more realistic modeling)
|
12 |
+
CREATE TABLE users (
|
13 |
+
user_id INTEGER PRIMARY KEY,
|
14 |
+
name TEXT NOT NULL,
|
15 |
+
email TEXT,
|
16 |
+
membership_date DATE
|
17 |
+
);
|
18 |
+
|
19 |
+
-- Loans table
|
20 |
+
CREATE TABLE loans (
|
21 |
+
loan_id INTEGER PRIMARY KEY,
|
22 |
+
book_id INTEGER,
|
23 |
+
user_id INTEGER,
|
24 |
+
issue_date DATE,
|
25 |
+
due_date DATE,
|
26 |
+
return_date DATE,
|
27 |
+
FOREIGN KEY (book_id) REFERENCES books(book_id),
|
28 |
+
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
29 |
+
);
|
30 |
+
|
31 |
+
INSERT INTO books (book_id, title, author, genre, year, available_copies) VALUES
|
32 |
+
(1, 'The Great Gatsby', 'F. Scott Fitzgerald', 'Fiction', 1925, 2),
|
33 |
+
(2, '1984', 'George Orwell', 'Dystopian', 1949, 3),
|
34 |
+
(3, 'Dune', 'Frank Herbert', 'Science Fiction', 1965, 4),
|
35 |
+
(4, 'Project Hail Mary', 'Andy Weir', 'Science Fiction', 2021, 1),
|
36 |
+
(5, 'Klara and the Sun', 'Kazuo Ishiguro', 'Science Fiction', 2021, 2),
|
37 |
+
(6, 'The Martian', 'Andy Weir', 'Science Fiction', 2011, 2),
|
38 |
+
(7, 'Animal Farm', 'George Orwell', 'Political Satire', 1945, 3);
|
39 |
+
|
40 |
+
|
41 |
+
INSERT INTO users (user_id, name, email, membership_date) VALUES
|
42 |
+
(101, 'Alice Johnson', 'alice.j@example.com', '2020-06-15'),
|
43 |
+
(102, 'Bob Smith', 'bob.smith@example.com', '2019-08-22'),
|
44 |
+
(103, 'Charlie Rose', 'charlie.r@example.com', '2021-01-10'),
|
45 |
+
(104, 'Diana Prince', 'diana.p@example.com', '2022-05-04'),
|
46 |
+
(105, 'Ethan Hunt', 'ethan.h@example.com', '2023-02-01');
|
47 |
+
|
48 |
+
|
49 |
+
INSERT INTO loans (loan_id, book_id, user_id, issue_date, due_date, return_date) VALUES
|
50 |
+
(1, 1, 101, '2022-11-30', '2022-12-15', '2022-12-12'),
|
51 |
+
(2, 2, 102, '2022-12-20', '2023-01-10', NULL),
|
52 |
+
(3, 3, 103, '2022-11-01', '2022-11-30', '2022-11-28'),
|
53 |
+
(4, 4, 104, '2023-01-10', '2023-02-01', NULL),
|
54 |
+
(5, 5, 105, '2023-02-10', '2023-03-01', NULL),
|
55 |
+
(6, 2, 101, '2023-01-15', '2023-02-05', '2023-02-04');
|
app/schemas/restaurant.sql
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Menu Items Table
|
2 |
+
CREATE TABLE menu_items (
|
3 |
+
item_id INTEGER PRIMARY KEY,
|
4 |
+
name TEXT NOT NULL,
|
5 |
+
category TEXT,
|
6 |
+
price REAL NOT NULL
|
7 |
+
);
|
8 |
+
|
9 |
+
-- Customers Table
|
10 |
+
CREATE TABLE customers (
|
11 |
+
customer_id INTEGER PRIMARY KEY,
|
12 |
+
name TEXT NOT NULL,
|
13 |
+
contact TEXT,
|
14 |
+
email TEXT
|
15 |
+
);
|
16 |
+
|
17 |
+
-- Orders Table
|
18 |
+
CREATE TABLE orders (
|
19 |
+
order_id INTEGER PRIMARY KEY,
|
20 |
+
customer_id INTEGER,
|
21 |
+
order_date DATE,
|
22 |
+
total_amount REAL,
|
23 |
+
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
|
24 |
+
);
|
25 |
+
|
26 |
+
-- Order Items Table
|
27 |
+
CREATE TABLE order_items (
|
28 |
+
order_item_id INTEGER PRIMARY KEY,
|
29 |
+
order_id INTEGER,
|
30 |
+
item_id INTEGER,
|
31 |
+
quantity INTEGER,
|
32 |
+
FOREIGN KEY (order_id) REFERENCES orders(order_id),
|
33 |
+
FOREIGN KEY (item_id) REFERENCES menu_items(item_id)
|
34 |
+
);
|
35 |
+
|
36 |
+
|
37 |
+
INSERT INTO menu_items VALUES
|
38 |
+
(1, 'Margherita Pizza', 'Main Course', 250.0),
|
39 |
+
(2, 'Paneer Tikka', 'Appetizer', 180.0),
|
40 |
+
(3, 'Chocolate Brownie', 'Dessert', 120.0),
|
41 |
+
(4, 'Cold Coffee', 'Beverage', 90.0),
|
42 |
+
(5, 'Veg Burger', 'Main Course', 150.0),
|
43 |
+
(6, 'French Fries', 'Appetizer', 100.0),
|
44 |
+
(7, 'Lemonade', 'Beverage', 60.0);
|
45 |
+
|
46 |
+
INSERT INTO customers VALUES
|
47 |
+
(1, 'Aman Gupta', '9876543210', 'aman.g@example.com'),
|
48 |
+
(2, 'Neha Sharma', '9876501234', 'neha.s@example.com'),
|
49 |
+
(3, 'Rohit Mehra', '9876567890', 'rohit.m@example.com');
|
50 |
+
|
51 |
+
INSERT INTO orders VALUES
|
52 |
+
(1, 1, '2023-06-10', 500.0),
|
53 |
+
(2, 2, '2023-06-11', 360.0),
|
54 |
+
(3, 1, '2023-06-15', 420.0),
|
55 |
+
(4, 3, '2023-06-17', 270.0);
|
56 |
+
|
57 |
+
INSERT INTO order_items VALUES
|
58 |
+
(1, 1, 1, 2), -- 2 Margherita Pizzas
|
59 |
+
(2, 2, 2, 1), -- 1 Paneer Tikka
|
60 |
+
(3, 2, 4, 2), -- 2 Cold Coffees
|
61 |
+
(4, 3, 5, 1), -- 1 Veg Burger
|
62 |
+
(5, 3, 3, 2), -- 2 Brownies
|
63 |
+
(6, 3, 4, 1), -- 1 Cold Coffee
|
64 |
+
(7, 4, 6, 2), -- 2 French Fries
|
65 |
+
(8, 4, 7, 1); -- 1 Lemonade
|
app/schemas/retail.sql
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-- Product inventory table
|
2 |
+
CREATE TABLE inventory (
|
3 |
+
product_id INTEGER PRIMARY KEY,
|
4 |
+
product_name TEXT NOT NULL,
|
5 |
+
category TEXT,
|
6 |
+
price REAL NOT NULL,
|
7 |
+
stock INTEGER NOT NULL,
|
8 |
+
reorder_level INTEGER DEFAULT 5
|
9 |
+
);
|
10 |
+
|
11 |
+
-- Restocks table
|
12 |
+
CREATE TABLE restocks (
|
13 |
+
restock_id INTEGER PRIMARY KEY,
|
14 |
+
product_id INTEGER,
|
15 |
+
restock_date DATE,
|
16 |
+
quantity INTEGER NOT NULL,
|
17 |
+
supplier TEXT,
|
18 |
+
FOREIGN KEY (product_id) REFERENCES inventory(product_id)
|
19 |
+
);
|
20 |
+
|
21 |
+
INSERT INTO inventory (product_id, product_name, category, price, stock, reorder_level) VALUES
|
22 |
+
(1, 'Laptop', 'Electronics', 1200.0, 5, 3),
|
23 |
+
(2, 'Phone', 'Electronics', 800.0, 20, 10),
|
24 |
+
(3, 'Tablet', 'Electronics', 600.0, 8, 5),
|
25 |
+
(4, 'Monitor', 'Accessories', 300.0, 15, 7),
|
26 |
+
(5, 'Keyboard', 'Accessories', 100.0, 30, 10),
|
27 |
+
(6, 'Mouse', 'Accessories', 50.0, 50, 20),
|
28 |
+
(7, 'Headphones', 'Accessories', 150.0, 25, 10),
|
29 |
+
(8, 'Webcam', 'Accessories', 250.0, 4, 5),
|
30 |
+
(9, 'Charger', 'Electronics', 60.0, 12, 8);
|
31 |
+
|
32 |
+
INSERT INTO restocks (restock_id, product_id, restock_date, quantity, supplier) VALUES
|
33 |
+
(1, 1, '2023-01-01', 10, 'TechWorld Supplies'),
|
34 |
+
(2, 2, '2023-01-02', 20, 'GadgetMart Inc.'),
|
35 |
+
(3, 3, '2022-12-15', 15, 'ElectroParts Ltd.'),
|
36 |
+
(4, 4, '2023-01-03', 30, 'DisplayX Pvt. Ltd.'),
|
37 |
+
(5, 5, '2023-01-10', 40, 'KeyTech Solutions'),
|
38 |
+
(6, 6, '2023-01-20', 50, 'Mousey Traders'),
|
39 |
+
(7, 7, '2023-02-01', 25, 'AudioGear Distributors'),
|
40 |
+
(8, 1, '2023-03-01', 5, 'TechWorld Supplies'),
|
41 |
+
(9, 8, '2023-03-15', 10, 'VisionTech Systems'),
|
42 |
+
(10, 9, '2023-04-01', 12, 'ElectroParts Ltd.');
|
app/static/ace.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
app/static/favicon.png
ADDED
![]() |
app/static/index.html
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>SQL Practice Platform</title>
|
7 |
+
<link rel="icon" href="/static/favicon.png" type="image/x-icon" />
|
8 |
+
<script src="https://cdn.tailwindcss.com/3.3.3"></script>
|
9 |
+
<script
|
10 |
+
src="/static/ace.js"
|
11 |
+
id="ace-script"
|
12 |
+
onerror="this.nextElementSibling.src='https://cdnjs.cloudflare.com/ajax/libs/ace-builds/1.35.0/ace.js';"
|
13 |
+
></script>
|
14 |
+
<script src="" id="ace-fallback"></script>
|
15 |
+
<link href="/static/style.css" rel="stylesheet" />
|
16 |
+
<style>
|
17 |
+
#schemaInfo {
|
18 |
+
max-height: calc(100vh - 200px);
|
19 |
+
overflow-y: auto;
|
20 |
+
}
|
21 |
+
</style>
|
22 |
+
</head>
|
23 |
+
<body class="bg-gray-100 min-h-screen">
|
24 |
+
<div class="max-w-full mx-auto p-4">
|
25 |
+
<h1 class="text-3xl font-bold mb-6 text-center text-blue-600">
|
26 |
+
SQL Practice Platform
|
27 |
+
</h1>
|
28 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 h-[calc(100vh-100px)]">
|
29 |
+
<div class="bg-white p-4 rounded-lg shadow-md">
|
30 |
+
<h2 class="text-xl font-semibold mb-4 text-green-600">Database</h2>
|
31 |
+
<select
|
32 |
+
id="domainSelect"
|
33 |
+
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4"
|
34 |
+
>
|
35 |
+
<option value="">Select Database</option>
|
36 |
+
</select>
|
37 |
+
<button
|
38 |
+
id="loadSchemaBtn"
|
39 |
+
class="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 disabled:bg-gray-400 mb-4"
|
40 |
+
disabled
|
41 |
+
>
|
42 |
+
Loading...
|
43 |
+
</button>
|
44 |
+
<button
|
45 |
+
id="showSchemaBtn"
|
46 |
+
class="w-full bg-gray-500 text-white p-2 rounded-md hover:bg-gray-600 mb-4 hidden"
|
47 |
+
>
|
48 |
+
Show Schema
|
49 |
+
</button>
|
50 |
+
<button
|
51 |
+
id="showTableBtn"
|
52 |
+
class="w-full bg-gray-500 text-white p-2 rounded-md hover:bg-gray-600 mb-4 hidden"
|
53 |
+
>
|
54 |
+
Show Table
|
55 |
+
</button>
|
56 |
+
<div
|
57 |
+
id="schemaInfo"
|
58 |
+
class="text-sm overflow-auto max-h-60 hidden"
|
59 |
+
></div>
|
60 |
+
</div>
|
61 |
+
<div class="bg-white p-4 rounded-lg shadow-md flex flex-col">
|
62 |
+
<h2 class="text-xl font-semibold mb-4 text-purple-600">SQL Editor</h2>
|
63 |
+
<div id="sqlEditor" class="w-full h-48 border rounded-md mb-4"></div>
|
64 |
+
<div class="flex space-x-4 mb-4">
|
65 |
+
<button
|
66 |
+
id="runQueryBtn"
|
67 |
+
class="bg-green-600 text-white p-2 rounded-md hover:bg-green-700 disabled:bg-gray-400"
|
68 |
+
>
|
69 |
+
Run
|
70 |
+
</button>
|
71 |
+
</div>
|
72 |
+
<h2 class="text-xl font-semibold mb-4 text-purple-600">Results</h2>
|
73 |
+
<div id="results" class="text-sm overflow-auto flex-grow"></div>
|
74 |
+
<div id="errorMessage" class="text-red-500 mt-2"></div>
|
75 |
+
</div>
|
76 |
+
<div class="bg-white p-4 rounded-lg shadow-md flex flex-col">
|
77 |
+
<h2 class="text-xl font-semibold mb-4 text-yellow-600">
|
78 |
+
Practice Question
|
79 |
+
</h2>
|
80 |
+
<select
|
81 |
+
id="difficultySelect"
|
82 |
+
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4"
|
83 |
+
disabled
|
84 |
+
>
|
85 |
+
<option value="">Select Difficulty</option>
|
86 |
+
<option value="Beginner">Beginner</option>
|
87 |
+
<option value="Intermediate">Intermediate</option>
|
88 |
+
<option value="Advanced">Advanced</option>
|
89 |
+
</select>
|
90 |
+
<div id="questionDetails" class="text-sm mb-4"></div>
|
91 |
+
<div
|
92 |
+
class="flex justify-between mb-4"
|
93 |
+
style="display: none"
|
94 |
+
id="navButtons"
|
95 |
+
>
|
96 |
+
<button
|
97 |
+
id="prevBtn"
|
98 |
+
class="bg-gray-500 text-white p-2 rounded-md hover:bg-gray-600"
|
99 |
+
>
|
100 |
+
<<
|
101 |
+
</button>
|
102 |
+
<button
|
103 |
+
id="nextBtn"
|
104 |
+
class="bg-gray-500 text-white p-2 rounded-md hover:bg-gray-600"
|
105 |
+
>
|
106 |
+
>>
|
107 |
+
</button>
|
108 |
+
</div>
|
109 |
+
<button
|
110 |
+
id="hintBtn"
|
111 |
+
class="w-full bg-gray-500 text-white p-2 rounded-md hover:bg-gray-600 mb-4"
|
112 |
+
style="display: none"
|
113 |
+
>
|
114 |
+
Show Hint
|
115 |
+
</button>
|
116 |
+
<button
|
117 |
+
id="solutionBtn"
|
118 |
+
class="w-full bg-gray-400 text-white p-2 rounded-md hover:bg-gray-500 mb-4"
|
119 |
+
style="display: none"
|
120 |
+
>
|
121 |
+
Show Solution
|
122 |
+
</button>
|
123 |
+
</div>
|
124 |
+
</div>
|
125 |
+
</div>
|
126 |
+
<script src="/static/script.js"></script>
|
127 |
+
</body>
|
128 |
+
</html>
|
app/static/script.js
ADDED
@@ -0,0 +1,603 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
let sessionId,
|
2 |
+
editor,
|
3 |
+
currentQuestions = [],
|
4 |
+
currentQuestionIndex = 0,
|
5 |
+
isSchemaVisible,
|
6 |
+
isTableVisible,
|
7 |
+
isHintVisible,
|
8 |
+
isSolutionVisible;
|
9 |
+
|
10 |
+
async function init() {
|
11 |
+
await new Promise((r) =>
|
12 |
+
document.readyState === "complete"
|
13 |
+
? r()
|
14 |
+
: window.addEventListener("load", r)
|
15 |
+
);
|
16 |
+
const sqlEditor = document.getElementById("sqlEditor");
|
17 |
+
if (!sqlEditor) throw new Error("SQL Editor element not found");
|
18 |
+
await new Promise((r) => setTimeout(r, 100));
|
19 |
+
if (typeof ace === "undefined")
|
20 |
+
throw new Error("Ace Editor library not loaded");
|
21 |
+
editor = ace.edit("sqlEditor");
|
22 |
+
editor.setTheme("ace/theme/monokai");
|
23 |
+
editor.session.setMode("ace/mode/sql");
|
24 |
+
editor.setOptions({ enableBasicAutocompletion: true, fontSize: "12px" });
|
25 |
+
editor.setValue("SELECT * FROM customers;");
|
26 |
+
const response = await fetch("/api/session", { method: "POST" });
|
27 |
+
if (!response.ok) throw new Error("Failed to create session");
|
28 |
+
sessionId = (await response.json()).session_id;
|
29 |
+
const domainResponse = await fetch("/api/databases");
|
30 |
+
if (!domainResponse.ok) throw new Error("Failed to fetch databases");
|
31 |
+
const domains = (await domainResponse.json()).databases;
|
32 |
+
const domainSelect = document.getElementById("domainSelect");
|
33 |
+
domainSelect.innerHTML = '<option value="">Select Database</option>';
|
34 |
+
domains.forEach((domain) => {
|
35 |
+
const option = document.createElement("option");
|
36 |
+
option.value = domain;
|
37 |
+
option.textContent = domain.charAt(0).toUpperCase() + domain.slice(1);
|
38 |
+
domainSelect.appendChild(option);
|
39 |
+
});
|
40 |
+
document
|
41 |
+
.getElementById("loadSchemaBtn")
|
42 |
+
.addEventListener("click", loadDomain);
|
43 |
+
document
|
44 |
+
.getElementById("showSchemaBtn")
|
45 |
+
.addEventListener("click", showSchema);
|
46 |
+
document.getElementById("showTableBtn").addEventListener("click", showTable);
|
47 |
+
document
|
48 |
+
.getElementById("difficultySelect")
|
49 |
+
.addEventListener("change", loadQuestions);
|
50 |
+
document.getElementById("hintBtn").addEventListener("click", toggleHint);
|
51 |
+
document
|
52 |
+
.getElementById("solutionBtn")
|
53 |
+
.addEventListener("click", toggleSolution);
|
54 |
+
document.getElementById("prevBtn").addEventListener("click", prevQuestion);
|
55 |
+
document.getElementById("nextBtn").addEventListener("click", nextQuestion);
|
56 |
+
document.getElementById("runQueryBtn").addEventListener("click", runQuery);
|
57 |
+
}
|
58 |
+
|
59 |
+
async function loadDomain() {
|
60 |
+
const domain = document.getElementById("domainSelect").value;
|
61 |
+
if (!domain) {
|
62 |
+
showError("Please select a database.");
|
63 |
+
return;
|
64 |
+
}
|
65 |
+
const loadBtn = document.getElementById("loadSchemaBtn");
|
66 |
+
loadBtn.disabled = true;
|
67 |
+
loadBtn.textContent = "Loading...";
|
68 |
+
try {
|
69 |
+
const response = await fetch(`/api/load-schema/${domain}`, {
|
70 |
+
method: "POST",
|
71 |
+
headers: { "session-id": sessionId },
|
72 |
+
});
|
73 |
+
if (!response.ok)
|
74 |
+
throw new Error(
|
75 |
+
(await response.json()).detail || "Failed to load database"
|
76 |
+
);
|
77 |
+
await response.json();
|
78 |
+
document.getElementById("difficultySelect").disabled = false;
|
79 |
+
document.getElementById("showSchemaBtn").classList.remove("hidden");
|
80 |
+
document.getElementById("showTableBtn").classList.remove("hidden");
|
81 |
+
document.getElementById("schemaInfo").classList.add("hidden");
|
82 |
+
document.getElementById("questionDetails").innerHTML = "";
|
83 |
+
document.getElementById("hintBtn").style.display = "block";
|
84 |
+
document.getElementById("solutionBtn").style.display = "block";
|
85 |
+
currentQuestions = [];
|
86 |
+
currentQuestionIndex = 0;
|
87 |
+
} catch (e) {
|
88 |
+
showError(`Failed to load database: ${e.message}.`);
|
89 |
+
} finally {
|
90 |
+
loadBtn.disabled = false;
|
91 |
+
loadBtn.textContent = "Load Database";
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
async function showSchema() {
|
96 |
+
const domain = document.getElementById("domainSelect").value;
|
97 |
+
if (!domain) return;
|
98 |
+
const schemaInfo = document.getElementById("schemaInfo");
|
99 |
+
if (isTableVisible) {
|
100 |
+
isTableVisible = false;
|
101 |
+
schemaInfo.classList.add("hidden");
|
102 |
+
}
|
103 |
+
schemaInfo.classList.toggle("hidden");
|
104 |
+
isSchemaVisible = !schemaInfo.classList.contains("hidden");
|
105 |
+
if (isSchemaVisible) {
|
106 |
+
const schemaResponse = await fetch(`/api/schema/${domain}`, {
|
107 |
+
headers: { "session-id": sessionId },
|
108 |
+
});
|
109 |
+
if (!schemaResponse.ok)
|
110 |
+
throw new Error(
|
111 |
+
(await schemaResponse.json()).detail || "Failed to load schema"
|
112 |
+
);
|
113 |
+
const schema = (await schemaResponse.json()).schema;
|
114 |
+
let schemaHtml = '<div class="grid grid-cols-1 md:grid-cols-2 gap-4">';
|
115 |
+
for (const [table, columns] of Object.entries(schema)) {
|
116 |
+
schemaHtml += `<div class="bg-gray-100 p-2 rounded"><h3 class="font-semibold">${table}</h3><ul class="list-disc ml-4">`;
|
117 |
+
columns.forEach(
|
118 |
+
(col) => (schemaHtml += `<li>${col.name} (${col.type})</li>`)
|
119 |
+
);
|
120 |
+
schemaHtml += "</ul></div>";
|
121 |
+
}
|
122 |
+
schemaInfo.innerHTML = schemaHtml;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
async function showTable() {
|
127 |
+
const domain = document.getElementById("domainSelect").value;
|
128 |
+
if (!domain) return;
|
129 |
+
const schemaInfo = document.getElementById("schemaInfo");
|
130 |
+
if (isSchemaVisible) {
|
131 |
+
isSchemaVisible = false;
|
132 |
+
schemaInfo.classList.add("hidden");
|
133 |
+
}
|
134 |
+
schemaInfo.classList.toggle("hidden");
|
135 |
+
isTableVisible = !schemaInfo.classList.contains("hidden");
|
136 |
+
if (isTableVisible) {
|
137 |
+
const sampleResponse = await fetch(`/api/sample-data/${domain}`, {
|
138 |
+
headers: { "session-id": sessionId },
|
139 |
+
});
|
140 |
+
if (!sampleResponse.ok)
|
141 |
+
throw new Error(
|
142 |
+
(await sampleResponse.json()).detail || "Failed to load sample data"
|
143 |
+
);
|
144 |
+
const sampleData = (await sampleResponse.json()).sample_data;
|
145 |
+
let tableHtml = '<div class="grid grid-cols-1 gap-4">';
|
146 |
+
for (const [table, data] of Object.entries(sampleData)) {
|
147 |
+
tableHtml += `<div class="bg-gray-50 p-2 rounded"><h4 class="font-semibold">${table}</h4>`;
|
148 |
+
tableHtml += '<table class="w-full mt-2"><tr>';
|
149 |
+
data.columns.forEach(
|
150 |
+
(col) => (tableHtml += `<th class="border p-1">${col}</th>`)
|
151 |
+
);
|
152 |
+
tableHtml += "</tr>";
|
153 |
+
data.rows.forEach((row) => {
|
154 |
+
tableHtml += "<tr>";
|
155 |
+
data.columns.forEach(
|
156 |
+
(col) =>
|
157 |
+
(tableHtml += `<td class="border p-1">${row[col] || "NULL"}</td>`)
|
158 |
+
);
|
159 |
+
tableHtml += "</tr>";
|
160 |
+
});
|
161 |
+
tableHtml += "</table></div>";
|
162 |
+
}
|
163 |
+
schemaInfo.innerHTML = tableHtml;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
async function loadQuestions() {
|
168 |
+
const domain = document.getElementById("domainSelect").value;
|
169 |
+
const difficulty = document.getElementById("difficultySelect").value;
|
170 |
+
if (!domain) {
|
171 |
+
document.getElementById("difficultySelect").value = "";
|
172 |
+
alert("Please select and load a database first");
|
173 |
+
return;
|
174 |
+
}
|
175 |
+
if (!difficulty) {
|
176 |
+
document.getElementById("questionDetails").innerHTML = "";
|
177 |
+
document.getElementById("hintBtn").style.display = "none";
|
178 |
+
document.getElementById("solutionBtn").style.display = "none";
|
179 |
+
document.getElementById("navButtons").style.display = "none";
|
180 |
+
currentQuestions = [];
|
181 |
+
currentQuestionIndex = 0;
|
182 |
+
return;
|
183 |
+
}
|
184 |
+
const questionResponse = await fetch(
|
185 |
+
`/api/questions/${domain}?difficulty=${difficulty}`
|
186 |
+
);
|
187 |
+
if (!questionResponse.ok)
|
188 |
+
throw new Error(
|
189 |
+
(await questionResponse.json()).detail || "Failed to load questions"
|
190 |
+
);
|
191 |
+
currentQuestions = await questionResponse.json();
|
192 |
+
if (currentQuestions.length > 0) {
|
193 |
+
currentQuestionIndex = 0;
|
194 |
+
updateQuestionDisplay();
|
195 |
+
document.getElementById("hintBtn").style.display = "block";
|
196 |
+
document.getElementById("solutionBtn").style.display = "block";
|
197 |
+
document.getElementById("navButtons").style.display = "flex";
|
198 |
+
} else {
|
199 |
+
document.getElementById("questionDetails").innerHTML =
|
200 |
+
"<p>No questions available for this difficulty.</p>";
|
201 |
+
document.getElementById("hintBtn").style.display = "none";
|
202 |
+
document.getElementById("solutionBtn").style.display = "none";
|
203 |
+
document.getElementById("navButtons").style.display = "none";
|
204 |
+
currentQuestions = [];
|
205 |
+
currentQuestionIndex = 0;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
function updateQuestionDisplay() {
|
210 |
+
const questionDetails = document.getElementById("questionDetails");
|
211 |
+
if (
|
212 |
+
currentQuestions.length &&
|
213 |
+
currentQuestionIndex >= 0 &&
|
214 |
+
currentQuestionIndex < currentQuestions.length
|
215 |
+
) {
|
216 |
+
const question = currentQuestions[currentQuestionIndex];
|
217 |
+
questionDetails.innerHTML = `<p id="questionText"><strong>Practice Question:</strong> ${
|
218 |
+
question.description || "No question available."
|
219 |
+
}</p>`;
|
220 |
+
} else {
|
221 |
+
questionDetails.innerHTML =
|
222 |
+
'<p id="questionText">No questions available.</p>';
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
function prevQuestion() {
|
227 |
+
if (currentQuestions.length && currentQuestionIndex > 0) {
|
228 |
+
currentQuestionIndex--;
|
229 |
+
updateQuestionDisplay();
|
230 |
+
updateHintSolutionDisplay();
|
231 |
+
}
|
232 |
+
}
|
233 |
+
function nextQuestion() {
|
234 |
+
if (
|
235 |
+
currentQuestions.length &&
|
236 |
+
currentQuestionIndex < currentQuestions.length - 1
|
237 |
+
) {
|
238 |
+
currentQuestionIndex++;
|
239 |
+
updateQuestionDisplay();
|
240 |
+
updateHintSolutionDisplay();
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
function updateHintSolutionDisplay() {
|
245 |
+
if (isHintVisible) toggleHint();
|
246 |
+
if (isSolutionVisible) toggleSolution();
|
247 |
+
}
|
248 |
+
|
249 |
+
function toggleHint() {
|
250 |
+
const question = currentQuestions[currentQuestionIndex];
|
251 |
+
const hintBtn = document.getElementById("hintBtn");
|
252 |
+
const questionDetails = document.getElementById("questionDetails");
|
253 |
+
if (isSolutionVisible) toggleSolution();
|
254 |
+
if (question && question.hint) {
|
255 |
+
if (hintBtn.textContent === "Show Hint") {
|
256 |
+
questionDetails.innerHTML += `<p><strong>Hint:</strong> ${question.hint}</p>`;
|
257 |
+
hintBtn.textContent = "Hide Hint";
|
258 |
+
isHintVisible = true;
|
259 |
+
} else {
|
260 |
+
questionDetails.innerHTML = questionDetails.innerHTML.replace(
|
261 |
+
`<p><strong>Hint:</strong> ${question.hint}</p>`,
|
262 |
+
""
|
263 |
+
);
|
264 |
+
hintBtn.textContent = "Show Hint";
|
265 |
+
isHintVisible = false;
|
266 |
+
}
|
267 |
+
} else {
|
268 |
+
if (hintBtn.textContent === "Show Hint") {
|
269 |
+
questionDetails.innerHTML +=
|
270 |
+
'<p class="text-black">No hint available.</p>';
|
271 |
+
hintBtn.textContent = "Hide Hint";
|
272 |
+
isHintVisible = true;
|
273 |
+
} else {
|
274 |
+
questionDetails.innerHTML = questionDetails.innerHTML.replace(
|
275 |
+
'<p class="text-black">No hint available.</p>',
|
276 |
+
""
|
277 |
+
);
|
278 |
+
hintBtn.textContent = "Show Hint";
|
279 |
+
isHintVisible = false;
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
function toggleSolution() {
|
285 |
+
const question = currentQuestions[currentQuestionIndex];
|
286 |
+
const solutionBtn = document.getElementById("solutionBtn");
|
287 |
+
const questionDetails = document.getElementById("questionDetails");
|
288 |
+
if (isHintVisible) toggleHint();
|
289 |
+
if (question && question.expected_sql) {
|
290 |
+
if (solutionBtn.textContent === "Show Solution") {
|
291 |
+
questionDetails.innerHTML += `<p><strong>Solution:</strong> <code>${question.expected_sql}</code></p>`;
|
292 |
+
solutionBtn.textContent = "Hide Solution";
|
293 |
+
isSolutionVisible = true;
|
294 |
+
} else {
|
295 |
+
questionDetails.innerHTML = questionDetails.innerHTML.replace(
|
296 |
+
`<p><strong>Solution:</strong> <code>${question.expected_sql}</code></p>`,
|
297 |
+
""
|
298 |
+
);
|
299 |
+
solutionBtn.textContent = "Show Solution";
|
300 |
+
isSolutionVisible = false;
|
301 |
+
}
|
302 |
+
} else {
|
303 |
+
if (solutionBtn.textContent === "Show Solution") {
|
304 |
+
questionDetails.innerHTML +=
|
305 |
+
'<p class="text-black">No solution available.</p>';
|
306 |
+
solutionBtn.textContent = "Hide Solution";
|
307 |
+
isSolutionVisible = true;
|
308 |
+
} else {
|
309 |
+
questionDetails.innerHTML = questionDetails.innerHTML.replace(
|
310 |
+
'<p class="text-black">No solution available.</p>',
|
311 |
+
""
|
312 |
+
);
|
313 |
+
solutionBtn.textContent = "Show Solution";
|
314 |
+
isSolutionVisible = false;
|
315 |
+
}
|
316 |
+
}
|
317 |
+
}
|
318 |
+
|
319 |
+
async function runQuery() {
|
320 |
+
const runBtn = document.getElementById("runQueryBtn");
|
321 |
+
const resultsDiv = document.getElementById("results");
|
322 |
+
let resultMessage =
|
323 |
+
document.getElementById("resultMessage") || document.createElement("span");
|
324 |
+
if (!resultMessage.id) {
|
325 |
+
resultMessage.id = "resultMessage";
|
326 |
+
runBtn.parentNode.appendChild(resultMessage);
|
327 |
+
}
|
328 |
+
runBtn.disabled = true;
|
329 |
+
runBtn.textContent = "Running...";
|
330 |
+
resultMessage.textContent = "";
|
331 |
+
try {
|
332 |
+
if (!editor) throw new Error("Editor not initialized. Refresh the page.");
|
333 |
+
let query = editor.getValue().trim().toLowerCase();
|
334 |
+
if (!query) throw new Error("Please enter a query.");
|
335 |
+
const domain = document.getElementById("domainSelect").value;
|
336 |
+
if (!domain) throw new Error("Please load a database first.");
|
337 |
+
const schemaResponse = await fetch(`/api/schema/${domain}`, {
|
338 |
+
headers: { "session-id": sessionId },
|
339 |
+
});
|
340 |
+
if (!schemaResponse.ok)
|
341 |
+
throw new Error("Failed to load schema for table validation");
|
342 |
+
const schema = (await schemaResponse.json()).schema;
|
343 |
+
const validTables = Object.keys(schema).map((t) => t.toLowerCase());
|
344 |
+
const tableNames = extractTableNames(query);
|
345 |
+
if (tableNames.some((table) => !validTables.includes(table)))
|
346 |
+
throw new Error(
|
347 |
+
`Invalid table name. Use only: ${validTables.join(", ")}`
|
348 |
+
);
|
349 |
+
const response = await fetch("/api/run-query", {
|
350 |
+
method: "POST",
|
351 |
+
headers: { "Content-Type": "application/json", "session-id": sessionId },
|
352 |
+
body: JSON.stringify({ query }),
|
353 |
+
});
|
354 |
+
if (!response.ok) {
|
355 |
+
const errorText = await response.text();
|
356 |
+
throw new Error(errorText || "Server error occurred.");
|
357 |
+
}
|
358 |
+
const result = await response.json();
|
359 |
+
if (result.columns) {
|
360 |
+
let html = `<table class="w-full border-collapse"><tr>${result.columns
|
361 |
+
.map((col) => `<th class="border p-2">${col}</th>`)
|
362 |
+
.join("")}</tr>`;
|
363 |
+
html += result.rows
|
364 |
+
.map(
|
365 |
+
(row) =>
|
366 |
+
`<tr>${result.columns
|
367 |
+
.map((col) => `<td class="border p-2">${row[col] || "NULL"}</td>`)
|
368 |
+
.join("")}</tr>`
|
369 |
+
)
|
370 |
+
.join("");
|
371 |
+
html += "</table>";
|
372 |
+
resultsDiv.innerHTML = html;
|
373 |
+
} else {
|
374 |
+
resultsDiv.innerHTML = "<p>No results</p>";
|
375 |
+
}
|
376 |
+
if (
|
377 |
+
currentQuestions.length &&
|
378 |
+
currentQuestionIndex >= 0 &&
|
379 |
+
currentQuestionIndex < currentQuestions.length
|
380 |
+
)
|
381 |
+
await validateQuery(query, result, resultsDiv);
|
382 |
+
else {
|
383 |
+
resultMessage.textContent = "Select a question first";
|
384 |
+
resultMessage.className = "text-red-500 ml-4";
|
385 |
+
}
|
386 |
+
} catch (e) {
|
387 |
+
resultsDiv.innerHTML = "";
|
388 |
+
resultMessage.textContent = e.message.includes("Internal Server Error")
|
389 |
+
? "Server error: Please check the query or try again later."
|
390 |
+
: e.message;
|
391 |
+
resultMessage.className = "text-red-500 ml-4";
|
392 |
+
} finally {
|
393 |
+
runBtn.disabled = false;
|
394 |
+
runBtn.textContent = "Run";
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
function extractTableNames(query) {
|
399 |
+
const tables = new Set();
|
400 |
+
const tokens = query.replace(/(\s+)/g, " ").split(" ");
|
401 |
+
let inSubquery = false,
|
402 |
+
inOpenQuery = false,
|
403 |
+
inValues = false;
|
404 |
+
|
405 |
+
for (let i = 0; i < tokens.length; i++) {
|
406 |
+
const token = tokens[i].toLowerCase();
|
407 |
+
if (token === "(" && !inSubquery && !inValues) {
|
408 |
+
if (i > 0 && tokens[i - 1].toLowerCase() === "values") inValues = true;
|
409 |
+
else inSubquery = true;
|
410 |
+
}
|
411 |
+
if (token === ")" && (inSubquery || inValues)) {
|
412 |
+
if (
|
413 |
+
inValues &&
|
414 |
+
i + 1 < tokens.length &&
|
415 |
+
tokens[i + 1].toLowerCase() === "as"
|
416 |
+
)
|
417 |
+
inValues = false;
|
418 |
+
else if (inSubquery) inSubquery = false;
|
419 |
+
}
|
420 |
+
if (token === "openquery" && i + 1 < tokens.length && tokens[i + 1] === "(")
|
421 |
+
inOpenQuery = true;
|
422 |
+
if (token === ")" && inOpenQuery) inOpenQuery = false;
|
423 |
+
if (inOpenQuery) continue;
|
424 |
+
|
425 |
+
if (
|
426 |
+
[
|
427 |
+
"from",
|
428 |
+
"join",
|
429 |
+
"update",
|
430 |
+
"delete",
|
431 |
+
"insert",
|
432 |
+
"into",
|
433 |
+
"using",
|
434 |
+
"apply",
|
435 |
+
"pivot",
|
436 |
+
"table",
|
437 |
+
].includes(token)
|
438 |
+
) {
|
439 |
+
let nextToken = tokens[i + 1]
|
440 |
+
? tokens[i + 1].replace(/[,;)]/g, "").toLowerCase()
|
441 |
+
: "";
|
442 |
+
if (
|
443 |
+
nextToken &&
|
444 |
+
![
|
445 |
+
"select",
|
446 |
+
"where",
|
447 |
+
"on",
|
448 |
+
"order",
|
449 |
+
"group",
|
450 |
+
"having",
|
451 |
+
"as",
|
452 |
+
"(",
|
453 |
+
].includes(nextToken)
|
454 |
+
) {
|
455 |
+
if (i + 2 < tokens.length && tokens[i + 2].toLowerCase() === "as")
|
456 |
+
nextToken = nextToken;
|
457 |
+
else if (
|
458 |
+
!["left", "right", "inner", "outer", "cross", "full"].includes(
|
459 |
+
nextToken
|
460 |
+
)
|
461 |
+
)
|
462 |
+
tables.add(nextToken);
|
463 |
+
}
|
464 |
+
i++;
|
465 |
+
} else if (
|
466 |
+
token === "merge" &&
|
467 |
+
i + 1 < tokens.length &&
|
468 |
+
tokens[i + 1].toLowerCase() === "into"
|
469 |
+
) {
|
470 |
+
let nextToken = tokens[i + 2]
|
471 |
+
? tokens[i + 2].replace(/[,;)]/g, "").toLowerCase()
|
472 |
+
: "";
|
473 |
+
if (nextToken && !["using", "select", "where"].includes(nextToken))
|
474 |
+
tables.add(nextToken);
|
475 |
+
i += 2;
|
476 |
+
while (i + 1 < tokens.length && tokens[i + 1].toLowerCase() !== "using")
|
477 |
+
i++;
|
478 |
+
if (i + 2 < tokens.length) {
|
479 |
+
nextToken = tokens[i + 2].replace(/[,;)]/g, "").toLowerCase();
|
480 |
+
if (nextToken && !["select", "where"].includes(nextToken))
|
481 |
+
tables.add(nextToken);
|
482 |
+
}
|
483 |
+
} else if (
|
484 |
+
token === "select" &&
|
485 |
+
i + 1 < tokens.length &&
|
486 |
+
tokens[i + 1].toLowerCase() === "into"
|
487 |
+
) {
|
488 |
+
let nextToken = tokens[i + 2]
|
489 |
+
? tokens[i + 2].replace(/[,;)]/g, "").toLowerCase()
|
490 |
+
: "";
|
491 |
+
if (nextToken && !["from", "select"].includes(nextToken))
|
492 |
+
tables.add(nextToken);
|
493 |
+
i += 2;
|
494 |
+
while (i + 1 < tokens.length && tokens[i + 1].toLowerCase() !== "from")
|
495 |
+
i++;
|
496 |
+
if (i + 2 < tokens.length) {
|
497 |
+
nextToken = tokens[i + 2].replace(/[,;)]/g, "").toLowerCase();
|
498 |
+
if (nextToken && !["where", "join"].includes(nextToken))
|
499 |
+
tables.add(nextToken);
|
500 |
+
}
|
501 |
+
} else if (token === "with") {
|
502 |
+
let cteStart = i + 1;
|
503 |
+
while (i + 1 < tokens.length && tokens[i + 1].toLowerCase() !== "as") i++;
|
504 |
+
if (i + 2 < tokens.length && tokens[i + 2] === "(") {
|
505 |
+
let bracketCount = 1,
|
506 |
+
subqueryStart = i + 2;
|
507 |
+
while (i + 1 < tokens.length && bracketCount > 0) {
|
508 |
+
i++;
|
509 |
+
if (tokens[i] === "(") bracketCount++;
|
510 |
+
if (tokens[i] === ")") bracketCount--;
|
511 |
+
}
|
512 |
+
const subquery = tokens.slice(subqueryStart, i).join(" ");
|
513 |
+
tables.add(
|
514 |
+
...extractTableNames(subquery).filter((t) => !tables.has(t))
|
515 |
+
);
|
516 |
+
}
|
517 |
+
} else if (
|
518 |
+
token === "values" &&
|
519 |
+
i + 1 < tokens.length &&
|
520 |
+
tokens[i + 1] === "("
|
521 |
+
) {
|
522 |
+
let aliasStart = i + 1;
|
523 |
+
while (i + 1 < tokens.length && tokens[i + 1] !== "as") i++;
|
524 |
+
if (i + 2 < tokens.length) {
|
525 |
+
let alias = tokens[i + 2].replace(/[,;)]/g, "").toLowerCase();
|
526 |
+
if (alias) tables.add(alias);
|
527 |
+
}
|
528 |
+
} else if (["exists", "in"].includes(token)) {
|
529 |
+
let subqueryStart = i + 1;
|
530 |
+
while (i + 1 < tokens.length && tokens[i + 1] !== ")") i++;
|
531 |
+
if (i > subqueryStart) {
|
532 |
+
const subquery = tokens.slice(subqueryStart, i + 1).join(" ");
|
533 |
+
tables.add(
|
534 |
+
...extractTableNames(subquery).filter((t) => !tables.has(t))
|
535 |
+
);
|
536 |
+
}
|
537 |
+
}
|
538 |
+
}
|
539 |
+
return Array.from(tables);
|
540 |
+
}
|
541 |
+
|
542 |
+
async function validateQuery(query, runResult, resultsDiv) {
|
543 |
+
const question = currentQuestions[currentQuestionIndex];
|
544 |
+
if (!question || !question.expected_sql) {
|
545 |
+
showError("No question or expected SQL available for validation.");
|
546 |
+
return;
|
547 |
+
}
|
548 |
+
const response = await fetch("/api/validate", {
|
549 |
+
method: "POST",
|
550 |
+
headers: { "Content-Type": "application/json", "session-id": sessionId },
|
551 |
+
body: JSON.stringify({
|
552 |
+
user_query: query,
|
553 |
+
expected_query: question.expected_sql,
|
554 |
+
}),
|
555 |
+
});
|
556 |
+
if (!response.ok)
|
557 |
+
throw new Error((await response.json()).detail || "Failed to validate");
|
558 |
+
const result = await response.json();
|
559 |
+
const questionText = document.getElementById("questionText");
|
560 |
+
const resultMessage = document.getElementById("resultMessage");
|
561 |
+
if (result.valid) {
|
562 |
+
questionText.classList.remove("text-red-500");
|
563 |
+
questionText.classList.add("text-green-500");
|
564 |
+
resultMessage.textContent = "Correct answer!";
|
565 |
+
resultMessage.className = "text-green-500 ml-4";
|
566 |
+
if (runResult.columns) {
|
567 |
+
let html = `<table class="w-full border-collapse"><tr>${runResult.columns
|
568 |
+
.map((col) => `<th class="border p-2">${col}</th>`)
|
569 |
+
.join("")}</tr>`;
|
570 |
+
html += runResult.rows
|
571 |
+
.map(
|
572 |
+
(row) =>
|
573 |
+
`<tr>${runResult.columns
|
574 |
+
.map((col) => `<td class="border p-2">${row[col] || "NULL"}</td>`)
|
575 |
+
.join("")}</tr>`
|
576 |
+
)
|
577 |
+
.join("");
|
578 |
+
html += "</table>";
|
579 |
+
resultsDiv.innerHTML = html;
|
580 |
+
} else {
|
581 |
+
resultsDiv.innerHTML = "<p>No results</p>";
|
582 |
+
}
|
583 |
+
} else {
|
584 |
+
questionText.classList.remove("text-green-500");
|
585 |
+
questionText.classList.add("text-red-500");
|
586 |
+
resultMessage.textContent = "Incorrect answer!";
|
587 |
+
resultMessage.className = "text-red-500 ml-4";
|
588 |
+
resultsDiv.innerHTML = "";
|
589 |
+
}
|
590 |
+
}
|
591 |
+
|
592 |
+
function showError(message) {
|
593 |
+
let errorMessage =
|
594 |
+
document.getElementById("errorMessage") || document.createElement("span");
|
595 |
+
if (!errorMessage.id) {
|
596 |
+
errorMessage.id = "errorMessage";
|
597 |
+
document.getElementById("runQueryBtn").parentNode.appendChild(errorMessage);
|
598 |
+
}
|
599 |
+
errorMessage.textContent = message;
|
600 |
+
errorMessage.className = "text-red-500 mt-2";
|
601 |
+
}
|
602 |
+
|
603 |
+
window.onload = init;
|
app/static/style.css
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
font-family: Arial, sans-serif;
|
3 |
+
}
|
4 |
+
#results table {
|
5 |
+
border-collapse: collapse;
|
6 |
+
width: 100%;
|
7 |
+
}
|
8 |
+
#results th,
|
9 |
+
#results td {
|
10 |
+
border: 1px solid #ddd;
|
11 |
+
padding: 8px;
|
12 |
+
text-align: left;
|
13 |
+
}
|
14 |
+
#results th {
|
15 |
+
background-color: #f2f2f2;
|
16 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
fastapi==0.110.0
|
2 |
+
uvicorn
|
3 |
+
pydantic
|
4 |
+
sqlparse
|