Free tool

Free Online SQL Compiler — Powered by PostgreSQL

Write and run SQL against a realistic learning-platform schema — students, instructors, courses and enrollments — and practice everything from simple filters to recursive CTEs.

Powered by real Postgres, runs entirely in your browser

Practice PostgreSQL in your browser

This SQL compiler runs PostgreSQL inside your browser tab, so you can practice the SQL used in analytics interviews and day-to-day reporting without installing a database. Use it as a PostgreSQL playground for joins, aggregations, CTEs and window functions.

Write a query, press Run, and see results instantly. There is nothing to set up and no account is needed.

What you can practice

  • Filtering and sorting
  • Joins, including inner, left and anti-joins
  • GROUP BY and HAVING
  • Subqueries
  • CASE expressions
  • CTEs
  • Window functions, including RANK, LAG and running totals
  • Recursive CTEs

The sample database

TableColumnsWhat it holds
studentsstudent_id SERIAL, name TEXT, email TEXT, city TEXT, track TEXT, joined_date DATE18 learners, their location, track and joining date.
instructorsinstructor_id SERIAL, name TEXT, company TEXT5 instructors and their companies.
coursescourse_id SERIAL, title TEXT, track TEXT, instructor_id INT, duration_weeks INT9 courses, their tracks, instructors and durations.
enrollmentsenrollment_id SERIAL, student_id INT, course_id INT, enrolled_date DATE, completion_pct INT, score INT38 course enrollments with completion and score data.

Courses join to instructors through instructor_id. Enrollments join to students through student_id and to courses through course_id.

Example queries to try

Filter and sort

Learn how WHERE, ORDER BY and LIMIT narrow and arrange a result set.

SELECT name, city, joined_date
FROM students
WHERE city = 'Bengaluru'
ORDER BY joined_date DESC
LIMIT 10;
Open in editor

Join and aggregate

Count enrollments for each course by joining courses to enrollments.

SELECT c.title, COUNT(e.enrollment_id) AS enrollments
FROM courses c
JOIN enrollments e ON e.course_id = c.course_id
GROUP BY c.title
ORDER BY enrollments DESC;
Open in editor

Anti-join with LEFT JOIN

Find students not enrolled in a specific course by checking for an unmatched row.

SELECT s.name
FROM students s
LEFT JOIN enrollments e
  ON e.student_id = s.student_id
 AND e.course_id = 9
WHERE e.enrollment_id IS NULL;
Open in editor

HAVING

Filter grouped results after counting courses for each instructor.

SELECT i.name, COUNT(c.course_id) AS course_count
FROM instructors i
JOIN courses c ON c.instructor_id = i.instructor_id
GROUP BY i.name
HAVING COUNT(c.course_id) >= 2;
Open in editor

CASE expressions

Turn numeric course durations into clear learning bands.

SELECT title,
       duration_weeks,
       CASE
         WHEN duration_weeks <= 3 THEN 'Short'
         WHEN duration_weeks <= 4 THEN 'Standard'
         ELSE 'Extended'
       END AS duration_band
FROM courses
ORDER BY duration_weeks, title;
Open in editor

Common table expression

Break an aggregation into a named step before joining it to course details.

WITH course_counts AS (
  SELECT course_id, COUNT(*) AS enrollments
  FROM enrollments
  GROUP BY course_id
)
SELECT c.title, cc.enrollments
FROM course_counts cc
JOIN courses c ON c.course_id = cc.course_id
ORDER BY cc.enrollments DESC
LIMIT 3;
Open in editor

Window function: RANK

Rank courses within each instructor without collapsing the course rows.

SELECT i.name AS instructor,
       c.title,
       COUNT(e.enrollment_id) AS enrollments,
       RANK() OVER (
         PARTITION BY i.instructor_id
         ORDER BY COUNT(e.enrollment_id) DESC
       ) AS rank_in_instructor
FROM instructors i
JOIN courses c ON c.instructor_id = i.instructor_id
LEFT JOIN enrollments e ON e.course_id = c.course_id
GROUP BY i.instructor_id, i.name, c.course_id, c.title
ORDER BY instructor, rank_in_instructor;
Open in editor

Window function: LAG

Compare each month’s enrollment count with the previous month.

WITH monthly AS (
  SELECT DATE_TRUNC('month', enrolled_date) AS month,
         COUNT(*) AS enrollments
  FROM enrollments
  GROUP BY 1
)
SELECT month,
       enrollments,
       enrollments - LAG(enrollments) OVER (ORDER BY month) AS change_vs_prev_month
FROM monthly
ORDER BY month;
Open in editor

Recursive CTE

Generate a date series by repeatedly building on the previous row.

WITH RECURSIVE days AS (
  SELECT DATE '2026-01-01' AS day
  UNION ALL
  SELECT day + 1 FROM days WHERE day < DATE '2026-01-10'
)
SELECT day FROM days;
Open in editor

Frequently asked questions

What is the Skillancy SQL compiler?

A free, browser-based PostgreSQL playground. You write SQL, run it against a sample learning-platform database with students, instructors, courses and enrollments, and see the results instantly.

Is it really PostgreSQL?

Yes. It uses PGlite, a build of PostgreSQL compiled to WebAssembly, so you get real Postgres syntax, including window functions, CTEs and recursive queries.

Do I need to sign up or install anything?

No. Open the page and start running queries. No account and no installation are needed.

Is my SQL sent to a server?

No. Queries run inside your browser tab. Analytics records only whether a SQL tool run succeeds or fails, never your query or results.

Will my queries be saved?

Not automatically. Use the Share button to get a link that contains your query after the # symbol, or copy it before you close the tab.

Which SQL topics can I practice here?

Filtering and sorting, joins, GROUP BY and HAVING, subqueries, CASE expressions, CTEs, window functions such as RANK and LAG, and recursive CTEs.

Can I use this to prepare for SQL interviews?

Yes. Start with the example queries on this page, then try the 25 problems on our SQL Interview Questions page, which come with a live editor.

Keep practicing

Want to go deeper?

Our live cohorts take you from SQL fundamentals to dashboards, agentic analytics workflows and interview-ready case studies — with mentor feedback on every project.