/* * test_sql_challenge.prg — Real-world SQL Challenge Queries * * These are the kind of queries seen in SQL coding interviews, * LeetCode/HackerRank challenges, and production analytics. * Tests FiveSql's ability to handle complex, nested, multi-feature queries. * * Copyright (c) 2025-2026 Charles KWON (Charles KWON OhJun) * Email: charleskwonohjun@gmail.com * All rights reserved. */ #include "FiveSqlDef.ch" #include "hbclass.ch" STATIC s_nPass := 0 STATIC s_nFail := 0 STATIC s_nTotal := 0 PROCEDURE Main() ? "================================================================" ? " SQL Challenge Queries — Real-world Stress Test" ? "================================================================" ? SetupData() Challenge01_SecondHighestSalary() Challenge02_NthHighestPerDept() Challenge03_ConsecutiveNumbers() Challenge04_DeptVsCompanyAvg() Challenge05_EmployeeManagerSalary() Challenge06_CumulativeSum() Challenge07_GapAnalysis() Challenge08_PivotSimulation() Challenge09_SelfJoinHierarchy() Challenge10_TopNPerGroup() Challenge11_RunningRank() Challenge12_YoYGrowth() Challenge13_IslandGap() Challenge14_MedianApprox() Challenge15_TripleNested() CleanupData() ? ? "================================================================" ? " Pass: " + hb_ntos( s_nPass ) ? " Fail: " + hb_ntos( s_nFail ) ? " Total: " + hb_ntos( s_nTotal ) ? " Rate: " + hb_ntos( Int( s_nPass * 100 / Max( s_nTotal, 1 ) ) ) + "%" ? "================================================================" RETURN STATIC FUNCTION Assert( cLabel, lOK ) s_nTotal++ IF lOK s_nPass++ ? " PASS: " + cLabel ELSE s_nFail++ ? " FAIL: " + cLabel ENDIF RETURN lOK STATIC FUNCTION Rows( aR ) IF ValType( aR ) == "A" .AND. Len( aR ) >= 2 .AND. ValType( aR[ 2 ] ) == "A" RETURN Len( aR[ 2 ] ) ENDIF RETURN 0 STATIC FUNCTION Cell( aR, nRow, nCol ) IF ValType( aR ) == "A" .AND. Len( aR ) >= 2 .AND. ; nRow <= Len( aR[ 2 ] ) .AND. nCol <= Len( aR[ 2 ][ nRow ] ) RETURN aR[ 2 ][ nRow ][ nCol ] ENDIF RETURN NIL STATIC FUNCTION SetupData() /* employees: 12 rows with hierarchy + varied salaries */ FErase( "employees.dbf" ) dbCreate( "employees.dbf", { ; { "ID", "N", 10, 0 }, ; { "NAME", "C", 20, 0 }, ; { "DEPT", "C", 15, 0 }, ; { "SALARY", "N", 12, 2 }, ; { "MGR_ID", "N", 10, 0 }, ; { "HIRE_YEAR","N", 4, 0 } ; } ) USE employees.dbf NEW EXCLUSIVE dbAppend() ; FieldPut(1, 1) ; FieldPut(2,"Alice") ; FieldPut(3,"Eng") ; FieldPut(4,9000) ; FieldPut(5,0) ; FieldPut(6,2020) dbAppend() ; FieldPut(1, 2) ; FieldPut(2,"Bob") ; FieldPut(3,"Eng") ; FieldPut(4,8000) ; FieldPut(5,1) ; FieldPut(6,2020) dbAppend() ; FieldPut(1, 3) ; FieldPut(2,"Charlie"); FieldPut(3,"Eng") ; FieldPut(4,8000) ; FieldPut(5,1) ; FieldPut(6,2021) dbAppend() ; FieldPut(1, 4) ; FieldPut(2,"Diana") ; FieldPut(3,"Sales") ; FieldPut(4,7500) ; FieldPut(5,0) ; FieldPut(6,2019) dbAppend() ; FieldPut(1, 5) ; FieldPut(2,"Eve") ; FieldPut(3,"Sales") ; FieldPut(4,6000) ; FieldPut(5,4) ; FieldPut(6,2021) dbAppend() ; FieldPut(1, 6) ; FieldPut(2,"Frank") ; FieldPut(3,"Sales") ; FieldPut(4,5500) ; FieldPut(5,4) ; FieldPut(6,2022) dbAppend() ; FieldPut(1, 7) ; FieldPut(2,"Grace") ; FieldPut(3,"Mktg") ; FieldPut(4,7000) ; FieldPut(5,0) ; FieldPut(6,2020) dbAppend() ; FieldPut(1, 8) ; FieldPut(2,"Henry") ; FieldPut(3,"Mktg") ; FieldPut(4,6500) ; FieldPut(5,7) ; FieldPut(6,2021) dbAppend() ; FieldPut(1, 9) ; FieldPut(2,"Ivy") ; FieldPut(3,"HR") ; FieldPut(4,6000) ; FieldPut(5,0) ; FieldPut(6,2019) dbAppend() ; FieldPut(1,10) ; FieldPut(2,"Jack") ; FieldPut(3,"HR") ; FieldPut(4,5000) ; FieldPut(5,9) ; FieldPut(6,2022) dbAppend() ; FieldPut(1,11) ; FieldPut(2,"Kate") ; FieldPut(3,"Eng") ; FieldPut(4,7000) ; FieldPut(5,2) ; FieldPut(6,2022) dbAppend() ; FieldPut(1,12) ; FieldPut(2,"Leo") ; FieldPut(3,"Eng") ; FieldPut(4,9000) ; FieldPut(5,2) ; FieldPut(6,2023) dbCommit() CLOSE ALL /* orders: 20 rows with amounts and years */ FErase( "orders.dbf" ) dbCreate( "orders.dbf", { ; { "ID", "N", 10, 0 }, ; { "EMP_ID", "N", 10, 0 }, ; { "AMOUNT", "N", 12, 2 }, ; { "ORD_YEAR", "N", 4, 0 } ; } ) USE orders.dbf NEW EXCLUSIVE dbAppend() ; FieldPut(1, 1) ; FieldPut(2, 1) ; FieldPut(3,2500) ; FieldPut(4,2022) dbAppend() ; FieldPut(1, 2) ; FieldPut(2, 1) ; FieldPut(3,3000) ; FieldPut(4,2023) dbAppend() ; FieldPut(1, 3) ; FieldPut(2, 2) ; FieldPut(3,1500) ; FieldPut(4,2022) dbAppend() ; FieldPut(1, 4) ; FieldPut(2, 2) ; FieldPut(3,2000) ; FieldPut(4,2023) dbAppend() ; FieldPut(1, 5) ; FieldPut(2, 4) ; FieldPut(3,4000) ; FieldPut(4,2022) dbAppend() ; FieldPut(1, 6) ; FieldPut(2, 4) ; FieldPut(3,3500) ; FieldPut(4,2023) dbAppend() ; FieldPut(1, 7) ; FieldPut(2, 5) ; FieldPut(3,1000) ; FieldPut(4,2022) dbAppend() ; FieldPut(1, 8) ; FieldPut(2, 5) ; FieldPut(3,1200) ; FieldPut(4,2023) dbAppend() ; FieldPut(1, 9) ; FieldPut(2, 7) ; FieldPut(3,3000) ; FieldPut(4,2022) dbAppend() ; FieldPut(1,10) ; FieldPut(2, 7) ; FieldPut(3,2800) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,11) ; FieldPut(2, 9) ; FieldPut(3, 800) ; FieldPut(4,2022) dbAppend() ; FieldPut(1,12) ; FieldPut(2, 9) ; FieldPut(3, 900) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,13) ; FieldPut(2,11) ; FieldPut(3,1800) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,14) ; FieldPut(2,12) ; FieldPut(3,2200) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,15) ; FieldPut(2, 3) ; FieldPut(3,1700) ; FieldPut(4,2022) dbAppend() ; FieldPut(1,16) ; FieldPut(2, 3) ; FieldPut(3,2100) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,17) ; FieldPut(2, 6) ; FieldPut(3, 600) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,18) ; FieldPut(2, 8) ; FieldPut(3,1400) ; FieldPut(4,2022) dbAppend() ; FieldPut(1,19) ; FieldPut(2, 8) ; FieldPut(3,1600) ; FieldPut(4,2023) dbAppend() ; FieldPut(1,20) ; FieldPut(2,10) ; FieldPut(3, 500) ; FieldPut(4,2023) dbCommit() CLOSE ALL RETURN NIL STATIC FUNCTION CleanupData() dbCloseAll() FErase( "employees.dbf" ) FErase( "orders.dbf" ) RETURN NIL /* ==================================================================== * Challenge 1: Second Highest Salary (LeetCode #176) * Find the second highest distinct salary. * ==================================================================== */ STATIC PROCEDURE Challenge01_SecondHighestSalary() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT MAX(salary) AS second_highest " + ; "FROM employees " + ; "WHERE salary < (SELECT MAX(salary) FROM employees)" ) /* Max=9000, second=8000 */ Assert( "1. Second Highest Salary = 8000", ; Rows( aR ) == 1 .AND. Cell( aR, 1, 1 ) == 8000 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 1. Second Highest (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 2: Nth Highest Salary Per Department (LeetCode #185 variant) * Top 2 earners per department using DENSE_RANK. * ==================================================================== */ STATIC PROCEDURE Challenge02_NthHighestPerDept() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH ranked AS (" + ; "SELECT name, dept, salary, " + ; "DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rnk " + ; "FROM employees" + ; ") SELECT name, dept, salary FROM ranked WHERE rnk <= 2 ORDER BY dept, salary DESC" ) /* Eng: Alice=9000,Leo=9000(tie),Bob=8000,Charlie=8000(tie) but DENSE_RANK top 2 = rank1+rank2 * Sales: Diana=7500, Eve=6000 * Mktg: Grace=7000, Henry=6500 * HR: Ivy=6000, Jack=5000 */ Assert( "2. Top 2 per dept (DENSE_RANK)", Rows( aR ) >= 8 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 2. Top 2 per dept (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 3: Consecutive same values (LeetCode #180 variant) * Find employees who share the same salary as at least one other person. * ==================================================================== */ STATIC PROCEDURE Challenge03_ConsecutiveNumbers() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT DISTINCT salary FROM employees " + ; "WHERE salary IN (" + ; " SELECT salary FROM employees GROUP BY salary HAVING COUNT(*) > 1" + ; ") ORDER BY salary DESC" ) /* 9000 (Alice,Leo), 8000 (Bob,Charlie), 6000 (Eve,Ivy), 7000 (Grace,Kate) */ Assert( "3. Duplicate salaries: 4 distinct values", Rows( aR ) == 4 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 3. Duplicate salaries (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 4: Department vs Company Average (Analytics) * Compare each department's average salary to company average. * ==================================================================== */ STATIC PROCEDURE Challenge04_DeptVsCompanyAvg() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH dept_avg AS (" + ; " SELECT dept, AVG(salary) AS dept_salary FROM employees GROUP BY dept" + ; "), company AS (" + ; " SELECT AVG(salary) AS company_salary FROM employees" + ; ") SELECT d.dept, d.dept_salary, " + ; "CASE WHEN d.dept_salary > c.company_salary THEN 'Above' ELSE 'Below' END AS vs_avg " + ; "FROM dept_avg d, company c ORDER BY d.dept_salary DESC" ) /* 4 departments compared to company average */ Assert( "4. Dept vs Company avg: 4 depts", Rows( aR ) == 4 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 4. Dept vs Company avg (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 5: Employees earning more than their manager (LeetCode #181) * Self-join to compare employee salary with manager salary. * ==================================================================== */ STATIC PROCEDURE Challenge05_EmployeeManagerSalary() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT e.name AS employee, m.name AS manager, " + ; "e.salary AS emp_salary, m.salary AS mgr_salary " + ; "FROM employees e JOIN employees m ON e.mgr_id = m.id " + ; "WHERE e.salary > m.salary" ) /* Leo(9000) > Bob(8000), Kate(7000) > Bob(8000)? No. Check: Leo mgr=Bob(8000), Leo=9000>8000 YES */ Assert( "5. Employee > Manager salary", Rows( aR ) >= 1 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 5. Employee > Manager (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 6: Cumulative/Running Sum (Analytics) * Running total of salary within each department. * ==================================================================== */ STATIC PROCEDURE Challenge06_CumulativeSum() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT name, dept, salary, " + ; "SUM(salary) OVER (PARTITION BY dept ORDER BY salary) AS running_total " + ; "FROM employees ORDER BY dept, salary" ) Assert( "6. Running SUM by dept: 12 rows", Rows( aR ) == 12 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 6. Running SUM (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 7: Gap Analysis — Find departments with no orders * LEFT JOIN + IS NULL pattern (anti-join). * ==================================================================== */ STATIC PROCEDURE Challenge07_GapAnalysis() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT DISTINCT e.dept FROM employees e " + ; "WHERE e.dept NOT IN (" + ; " SELECT DISTINCT e2.dept FROM employees e2 " + ; " JOIN orders o ON e2.id = o.emp_id" + ; ")" ) /* All depts have at least one employee with orders, so result may be 0 */ Assert( "7. Depts without orders (anti-join)", Rows( aR ) >= 0 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 7. Gap analysis (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 8: Pivot Simulation with CASE * Count employees per department as columns using CASE aggregation. * ==================================================================== */ STATIC PROCEDURE Challenge08_PivotSimulation() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "SELECT " + ; "SUM(CASE WHEN dept = 'Eng' THEN 1 ELSE 0 END) AS eng, " + ; "SUM(CASE WHEN dept = 'Sales' THEN 1 ELSE 0 END) AS sales, " + ; "SUM(CASE WHEN dept = 'Mktg' THEN 1 ELSE 0 END) AS mktg, " + ; "SUM(CASE WHEN dept = 'HR' THEN 1 ELSE 0 END) AS hr " + ; "FROM employees" ) /* Eng=5, Sales=3, Mktg=2, HR=2 */ Assert( "8. Pivot CASE: Eng=5 Sales=3 Mktg=2 HR=2", ; Rows( aR ) == 1 .AND. Cell( aR, 1, 1 ) == 5 .AND. ; Cell( aR, 1, 2 ) == 3 .AND. Cell( aR, 1, 3 ) == 2 .AND. ; Cell( aR, 1, 4 ) == 2 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 8. Pivot CASE (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 9: Self-Join Hierarchy — Org tree with recursive CTE * Find all reports (direct and indirect) for each manager. * ==================================================================== */ STATIC PROCEDURE Challenge09_SelfJoinHierarchy() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH RECURSIVE org AS (" + ; "SELECT id, name, mgr_id, 1 AS depth FROM employees WHERE mgr_id = 0 " + ; "UNION ALL " + ; "SELECT e.id, e.name, e.mgr_id, o.depth + 1 " + ; "FROM employees e JOIN org o ON e.mgr_id = o.id" + ; ") SELECT name, depth FROM org ORDER BY depth, name" ) Assert( "9. Org hierarchy: 12 employees, depth 1 first", ; Rows( aR ) == 12 .AND. Cell( aR, 1, 2 ) == 1 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 9. Org hierarchy (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 10: Top N Per Group — ROW_NUMBER filter * Highest earner per department (exactly 1 per dept). * ==================================================================== */ STATIC PROCEDURE Challenge10_TopNPerGroup() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH ranked AS (" + ; "SELECT name, dept, salary, " + ; "ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn " + ; "FROM employees" + ; ") SELECT name, dept, salary FROM ranked WHERE rn = 1 ORDER BY salary DESC" ) /* 4 departments, 1 top earner each */ Assert( "10. Top 1 per dept: 4 rows", Rows( aR ) == 4 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 10. Top N per group (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 11: Running Rank — LAG + comparison * Show each employee's salary and the difference from previous. * ==================================================================== */ STATIC PROCEDURE Challenge11_RunningRank() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH lagged AS (" + ; "SELECT name, salary, " + ; "LAG(salary, 1) OVER (ORDER BY salary DESC) AS prev_sal " + ; "FROM employees" + ; ") SELECT name, salary, prev_sal, " + ; "salary - COALESCE(prev_sal, salary) AS diff " + ; "FROM lagged ORDER BY salary DESC" ) Assert( "11. LAG + diff via CTE: 12 rows", ; Rows( aR ) == 12 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 11. LAG + diff (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 12: Year-over-Year Growth (Analytics) * Compare 2023 vs 2022 order totals per employee. * ==================================================================== */ STATIC PROCEDURE Challenge12_YoYGrowth() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH y22 AS (" + ; " SELECT emp_id, SUM(amount) AS total FROM orders WHERE ord_year = 2022 GROUP BY emp_id" + ; "), y23 AS (" + ; " SELECT emp_id, SUM(amount) AS total FROM orders WHERE ord_year = 2023 GROUP BY emp_id" + ; ") SELECT e.name, y22.total AS yr2022, y23.total AS yr2023 " + ; "FROM employees e " + ; "JOIN y22 ON e.id = y22.emp_id " + ; "JOIN y23 ON e.id = y23.emp_id " + ; "ORDER BY e.name" ) Assert( "12. YoY growth: employees with both years", Rows( aR ) >= 5 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 12. YoY growth (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 13: Island & Gap — Fibonacci via recursive CTE * Generate Fibonacci numbers and find gaps. * ==================================================================== */ STATIC PROCEDURE Challenge13_IslandGap() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH RECURSIVE fib AS (" + ; "SELECT 1 AS n, 1 AS val, 0 AS prev " + ; "UNION ALL " + ; "SELECT n + 1, val + prev, val FROM fib WHERE n < 12" + ; ") SELECT n, val FROM fib ORDER BY n" ) /* 12 Fibonacci numbers: 1,1,2,3,5,8,13,21,34,55,89,144 */ Assert( "13. Fibonacci 12 terms: fib(12)=144", ; Rows( aR ) == 12 .AND. Cell( aR, 12, 2 ) == 144 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 13. Fibonacci (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 14: Median approximation using percentile position * Find the middle salary using ROW_NUMBER and COUNT. * ==================================================================== */ STATIC PROCEDURE Challenge14_MedianApprox() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH numbered AS (" + ; "SELECT salary, " + ; "ROW_NUMBER() OVER (ORDER BY salary) AS rn, " + ; "COUNT(*) OVER () AS total " + ; "FROM employees" + ; ") SELECT salary FROM numbered " + ; "WHERE rn = total / 2 + 1" ) /* 12 employees sorted by salary, median position = 7th */ Assert( "14. Median salary (position-based)", Rows( aR ) >= 1 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 14. Median approx (exception)" END SEQUENCE RETURN /* ==================================================================== * Challenge 15: Triple nested subquery + CTE + Window * Most complex: CTE -> Window -> Subquery filter -> JOIN -> ORDER BY * ==================================================================== */ STATIC PROCEDURE Challenge15_TripleNested() LOCAL aR dbCloseAll() BEGIN SEQUENCE aR := five_SQL( ; "WITH order_stats AS (" + ; " SELECT emp_id, SUM(amount) AS total, COUNT(*) AS cnt " + ; " FROM orders GROUP BY emp_id" + ; ") " + ; "SELECT e.name, e.dept, e.salary, s.total, " + ; "RANK() OVER (ORDER BY s.total DESC) AS order_rank " + ; "FROM employees e " + ; "JOIN order_stats s ON e.id = s.emp_id " + ; "WHERE e.salary > (SELECT AVG(salary) FROM employees) " + ; "ORDER BY s.total DESC" ) /* Employees with above-avg salary who also have orders, ranked by order total */ Assert( "15. CTE+Window+Subquery+JOIN: complex analytics", Rows( aR ) >= 1 ) RECOVER s_nTotal++ ; s_nFail++ ; ? " FAIL: 15. Triple nested (exception)" END SEQUENCE RETURN