Glossary/SQL Queries — SELECT, JOINs, Subqueries & CTEs
SQL & Databases

SQL Queries — SELECT, JOINs, Subqueries & CTEs

Retrieving exactly the data you need from one or multiple tables.


Definition

The SELECT statement retrieves data from one or more tables. JOINs combine rows from multiple tables on related columns. Subqueries nest one SELECT inside another. CTEs (Common Table Expressions) name subqueries for readability and reusability. Mastering SELECT with all JOIN types, WHERE conditions, ORDER BY, LIMIT, and subqueries is essential for every database professional.

SELECT statement execution order

Clause execution order and full SELECT anatomy

-- Written order:           Execution order:
-- SELECT                   1. FROM + JOINs
-- FROM                     2. WHERE
-- WHERE                    3. GROUP BY
-- GROUP BY                 4. HAVING
-- HAVING                   5. SELECT
-- ORDER BY                 6. DISTINCT
-- LIMIT                    7. ORDER BY
--                          8. LIMIT/OFFSET

-- Full example
SELECT
    d.DeptName,
    COUNT(s.StudentID) AS StudentCount,
    AVG(s.GPA)         AS AvgGPA,
    MAX(s.GPA)         AS TopGPA
FROM Student s
INNER JOIN Department d ON s.DeptID = d.DeptID
WHERE s.Status = 'Active'
  AND s.EnrollDate > '2022-01-01'
GROUP BY d.DeptName
HAVING AVG(s.GPA) > 3.0
ORDER BY AvgGPA DESC
LIMIT 5;

JOIN types — all six with examples

JOIN typeReturnsNULL behaviourUse case
INNER JOINRows with matching values in BOTH tablesNo NULLs for join columnsStudents WITH departments
LEFT OUTER JOINAll LEFT rows + matching RIGHT rowsRight-side NULL if no matchAll students — NULL if no dept
RIGHT OUTER JOINAll RIGHT rows + matching LEFT rowsLeft-side NULL if no matchAll departments — NULL if no students
FULL OUTER JOINAll rows from BOTH tablesNULLs on whichever side has no matchComplete view — unmatched on both sides
CROSS JOINCartesian product of both tablesNo join conditionEvery student-course combination
SELF JOINTable joined with itself using aliasesAs per join typeEmployees and their managers (same table)

All JOIN types on the same dataset

-- Student(StudentID, Name, DeptID)  Department(DeptID, DeptName)
-- Some students have DeptID=NULL, some departments have no students

-- INNER: only students WITH a department
SELECT s.Name, d.DeptName
FROM Student s INNER JOIN Department d ON s.DeptID = d.DeptID;

-- LEFT: all students, NULL DeptName if no department
SELECT s.Name, d.DeptName
FROM Student s LEFT JOIN Department d ON s.DeptID = d.DeptID;

-- CROSS: every student-department combination
-- 5 students × 3 departments = 15 rows
SELECT s.Name, d.DeptName FROM Student s CROSS JOIN Department d;

-- SELF: find students in the same department
SELECT s1.Name AS S1, s2.Name AS S2, d.DeptName
FROM Student s1
JOIN Student s2 ON s1.DeptID = s2.DeptID AND s1.StudentID < s2.StudentID
JOIN Department d ON s1.DeptID = d.DeptID;

Subqueries and CTEs

Correlated subquery, EXISTS, and CTEs

-- 1. Uncorrelated subquery (runs once)
SELECT Name, GPA FROM Student
WHERE GPA > (SELECT AVG(GPA) FROM Student);

-- 2. Correlated subquery (runs once per row of outer query)
-- Students with GPA above their department average
SELECT s.Name, s.GPA FROM Student s
WHERE s.GPA > (
    SELECT AVG(GPA) FROM Student WHERE DeptID = s.DeptID
);

-- 3. EXISTS (preferred over IN for large tables — short-circuits)
SELECT Name FROM Student s
WHERE EXISTS (
    SELECT 1 FROM Department d
    WHERE d.DeptID = s.DeptID AND d.Building = 'Tech Block'
);

-- 4. NOT EXISTS: students with no enrollment
SELECT Name FROM Student s
WHERE NOT EXISTS (SELECT 1 FROM Enrollment e WHERE e.StudentID = s.StudentID);

-- 5. CTE (Common Table Expression) — readable named subquery
WITH DeptAvg AS (
    SELECT DeptID, AVG(GPA) AS AvgGPA
    FROM Student WHERE Status = 'Active'
    GROUP BY DeptID
)
SELECT s.Name, s.GPA, d.DeptName
FROM Student s
JOIN Department d ON s.DeptID = d.DeptID
JOIN DeptAvg da ON s.DeptID = da.DeptID
WHERE s.GPA > da.AvgGPA
ORDER BY s.GPA DESC;

-- 6. Recursive CTE: org chart traversal
WITH RECURSIVE OrgChart AS (
    SELECT EmpID, Name, ManagerID, 1 AS Level
    FROM Employee WHERE ManagerID IS NULL          -- root (top manager)
    UNION ALL
    SELECT e.EmpID, e.Name, e.ManagerID, oc.Level + 1
    FROM Employee e JOIN OrgChart oc ON e.ManagerID = oc.EmpID
)
SELECT * FROM OrgChart ORDER BY Level, Name;

Practice questions

  1. Difference between WHERE and HAVING? (Answer: WHERE filters individual rows BEFORE grouping. HAVING filters groups AFTER GROUP BY. Aggregate functions (SUM, COUNT, AVG) cannot go in WHERE.)
  2. LEFT JOIN returns 100 rows, INNER JOIN returns 60 rows. What does this tell you? (Answer: 40 rows in the left table have no matching rows in the right table — those appear with NULLs in the LEFT JOIN only.)
  3. Is a correlated subquery more or less efficient than uncorrelated? (Answer: Less efficient — runs once per row of outer query. For 10,000 outer rows, runs 10,000 times. Use EXISTS or JOIN as alternatives.)
  4. CROSS JOIN between tables with 4 and 6 rows produces how many rows? (Answer: 4 × 6 = 24 rows — Cartesian product.)
  5. Find the second highest salary in SQL. (Answer: SELECT MAX(Salary) FROM Employee WHERE Salary < (SELECT MAX(Salary) FROM Employee). Or: SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT 1 OFFSET 1.)

On LumiChats

LumiChats generates any SQL query from plain English: 'Find all students who scored above the department average' → generates the correlated subquery automatically with step-by-step explanation.

Try it free

Try LumiChats for ₹69

39+ AI models. Study Mode with page-locked answers. Agent Mode with code execution. Pay only on days you use it.

Get Started — ₹69/day

Related Terms

4 terms