Lesson 51 of 60 – MySQL Subqueries
85%

MySQL Subqueries

A subquery is a SQL query written inside another SQL query. The inner query is called the subquery, and the outer query uses the result returned by the subquery.

Note: A subquery is usually written inside parentheses ( ). Subqueries can be used with SELECT, WHERE, FROM, HAVING, INSERT, UPDATE, and DELETE statements.

1. What is a Subquery?

A subquery is a query inside another query.

SELECT name
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

The inner query calculates the average fee, and the outer query finds students whose fee is greater than that average.

2. Why Use Subqueries?

Subqueries are useful when the result of one query is needed by another query.

  • Compare values with an average.
  • Find records above or below a calculated value.
  • Find records existing in another result set.
  • Find records that do not have related data.
  • Generate temporary result sets.
  • Filter grouped results.
  • Insert data based on another query.
  • Update data using another query.

3. Basic Subquery Syntax

SELECT column_name
FROM table_name
WHERE column_name operator (
    SELECT column_name
    FROM another_table
);

The inner SELECT is executed as part of the overall query, and its result is used by the outer query.

4. Simple Subquery Example

Suppose we want students whose fee is greater than the average fee.

SELECT
    name,
    fee
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

The subquery:

SELECT AVG(fee)
FROM students;

returns one value, which is then used by the outer query.

5. Scalar Subquery

A scalar subquery returns exactly one value.

SELECT
    name,
    fee
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

The AVG() function returns one value, so it can be compared directly using >.

Remember: A scalar subquery should return one value when used with operators such as =, >, <, >=, or <=.

6. Subquery with Equal (=)

You can use a subquery with the equality operator when the subquery returns one value.

SELECT
    name,
    fee
FROM students
WHERE fee = (
    SELECT MAX(fee)
    FROM students
);

This finds the student or students having the maximum fee.

7. Subquery with Greater Than (>)

SELECT
    name,
    fee
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

This returns students whose fee is greater than the average fee.

8. Subquery with Less Than (<)

SELECT
    name,
    fee
FROM students
WHERE fee < (
    SELECT AVG(fee)
    FROM students
);

This returns students whose fee is below the average fee.

9. Subquery with MAX()

MAX() can be used inside a subquery.

SELECT
    name,
    salary
FROM employees
WHERE salary = (
    SELECT MAX(salary)
    FROM employees
);

This finds employee records having the highest salary.

10. Subquery with MIN()

SELECT
    name,
    salary
FROM employees
WHERE salary = (
    SELECT MIN(salary)
    FROM employees
);

This finds employee records having the lowest salary.

11. Subquery with COUNT()

COUNT() can be used to calculate a value inside a subquery.

SELECT
    name
FROM students
WHERE student_id < (
    SELECT COUNT(*)
    FROM students
);

The subquery returns the total number of rows.

The example demonstrates the syntax, although comparing an ID with a row count is not normally a meaningful business rule.

12. Subquery with IN

IN is useful when a subquery returns multiple values.

SELECT
    name,
    course_id
FROM students
WHERE course_id IN (
    SELECT id
    FROM courses
    WHERE fee > 10000
);

This finds students enrolled in courses whose fee is greater than 10,000.

13. Subquery with NOT IN

NOT IN can find records whose value is not present in the subquery result.

SELECT
    name,
    course_id
FROM students
WHERE course_id NOT IN (
    SELECT id
    FROM courses
    WHERE status = 'Inactive'
);

This excludes students whose course belongs to the returned inactive-course IDs.

Important: Be careful with NOT IN when the subquery can return NULL values. In such cases, NOT EXISTS is often a safer alternative.

14. Subquery with EXISTS

EXISTS checks whether the subquery returns at least one row.

SELECT
    c.course_name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM students s
    WHERE s.course_id = c.id
);

This returns courses having at least one matching student.

15. Subquery with NOT EXISTS

NOT EXISTS checks that the subquery returns no rows.

SELECT
    c.course_name
FROM courses c
WHERE NOT EXISTS (
    SELECT 1
    FROM students s
    WHERE s.course_id = c.id
);

This finds courses without matching students.

16. Correlated Subquery

A correlated subquery refers to a column from the outer query.

SELECT
    e.employee_name,
    e.salary,
    e.department
FROM employees e
WHERE e.salary > (
    SELECT AVG(e2.salary)
    FROM employees e2
    WHERE e2.department = e.department
);

The inner query uses the department of the current outer employee.

17. Correlated vs Non-Correlated Subquery

Non-Correlated Correlated
Does not depend on the outer query References the outer query
Can generally be evaluated independently Depends on values from the current outer row
Example: overall average salary Example: average salary for each employee's department

18. Subquery in the SELECT Clause

A scalar subquery can appear in the SELECT list.

SELECT
    name,
    fee,
    (SELECT AVG(fee) FROM students) AS average_fee
FROM students;

The overall average fee is displayed alongside every student.

19. Subquery in the FROM Clause

A subquery in the FROM clause creates a temporary result set for the outer query. It is commonly called a derived table.

SELECT
    course_id,
    average_fee
FROM (
    SELECT
        course_id,
        AVG(fee) AS average_fee
    FROM students
    GROUP BY course_id
) AS course_summary;

The derived table must have an alias.

20. Subquery in the HAVING Clause

A subquery can be used in HAVING to compare grouped results.

SELECT
    course_id,
    AVG(fee) AS average_fee
FROM students
GROUP BY course_id
HAVING AVG(fee) > (
    SELECT AVG(fee)
    FROM students
);

This returns course groups whose average fee is greater than the overall average fee.

21. Subquery in INSERT

A subquery can provide data for an INSERT statement.

INSERT INTO selected_students
(student_id, name)
SELECT
    student_id,
    name
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

This inserts students whose fee is above the average into another table.

22. Subquery in UPDATE

A subquery can also be used to determine which rows should be updated.

UPDATE students
SET status = 'Premium'
WHERE fee > (
    SELECT average_fee
    FROM (
        SELECT AVG(fee) AS average_fee
        FROM students
    ) AS x
);

The derived table wrapper avoids the restriction involved in directly modifying and selecting from the same target table in this form.

23. Subquery in DELETE

Subqueries can help identify rows to delete.

DELETE FROM students
WHERE course_id IN (
    SELECT id
    FROM courses
    WHERE status = 'Deleted'
);

This removes students whose course IDs belong to courses marked as Deleted.

Always test the SELECT condition first before running a DELETE query.

24. Subquery Returning Multiple Rows

A subquery can return multiple rows.

SELECT
    name,
    course_id
FROM students
WHERE course_id IN (
    SELECT id
    FROM courses
    WHERE fee > 10000
);

IN is appropriate because the inner query can return several course IDs.

Using = with a subquery that returns multiple rows can produce a MySQL error.

25. Subquery Returning No Rows

A subquery may return no rows.

For example:

SELECT
    name
FROM students
WHERE course_id IN (
    SELECT id
    FROM courses
    WHERE course_name = 'Unknown Course'
);

If the inner query returns no course IDs, the outer query returns no matching students.

26. Common Subquery Mistakes

  • Forgetting parentheses around the subquery.
  • Using = when the subquery returns multiple rows.
  • Returning multiple columns where one value is expected.
  • Forgetting an alias for a derived table.
  • Using NOT IN when NULL values can cause unexpected results.
  • Writing an unnecessarily complicated nested query.
  • Not testing the inner query separately.
Tip: Run the subquery by itself first. Check its result before combining it with the outer query.

27. Subquery vs JOIN

Subquery JOIN
Places one query inside another Combines rows from tables
Useful for calculated or filtered result sets Useful for directly combining related records
Can use IN, EXISTS, scalar results, and derived tables Uses INNER, LEFT, RIGHT and other JOIN forms

Many tasks can be solved using either a JOIN or a subquery. The best approach depends on the problem and query structure.

28. Nested Subqueries

A subquery can contain another subquery.

SELECT
    name,
    salary
FROM employees
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department_id IN (
        SELECT department_id
        FROM departments
        WHERE status = 'Active'
    )
);

This example contains multiple levels of queries.

Tip: Nested subqueries can become difficult to read, so use clear aliases and break complex logic into smaller steps when appropriate.

29. Practical Student Report Using Subqueries

Suppose we want to find students who have paid more than the average payment.

SELECT
    s.student_id,
    s.name,
    p.amount
FROM students s
INNER JOIN payments p
ON s.student_id = p.student_id
WHERE p.amount > (
    SELECT AVG(amount)
    FROM payments
);

The inner query calculates the average payment, and the outer query displays payments greater than that average.

30. Complete Subquery Example

CREATE TABLE courses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    course_name VARCHAR(100),
    fee DECIMAL(10,2),
    status VARCHAR(20)
);

CREATE TABLE students (
    student_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    course_id INT,
    fee DECIMAL(10,2),
    status VARCHAR(20)
);

CREATE TABLE payments (
    payment_id INT AUTO_INCREMENT PRIMARY KEY,
    student_id INT,
    amount DECIMAL(10,2),
    payment_date DATE
);

INSERT INTO courses
(course_name, fee, status)
VALUES
('Python', 15000, 'Active'),
('Java', 18000, 'Active'),
('PHP', 12000, 'Active'),
('SQL', 10000, 'Active');

INSERT INTO students
(name, course_id, fee, status)
VALUES
('Amit Kumar', 1, 15000, 'Active'),
('Priya Singh', 2, 18000, 'Active'),
('Rahul Sharma', 1, 15000, 'Active'),
('Neha Kumari', 3, 12000, 'Active'),
('Ravi Kumar', 4, 10000, 'Active');

INSERT INTO payments
(student_id, amount, payment_date)
VALUES
(1, 5000, '2026-01-10'),
(1, 4000, '2026-02-10'),
(2, 18000, '2026-03-15'),
(3, 7000, '2026-04-20'),
(4, 6000, '2026-05-10');

-- Students whose fee is above average
SELECT
    name,
    fee
FROM students
WHERE fee > (
    SELECT AVG(fee)
    FROM students
);

-- Students enrolled in courses costing more than 12000
SELECT
    name,
    course_id
FROM students
WHERE course_id IN (
    SELECT id
    FROM courses
    WHERE fee > 12000
);

-- Courses having at least one student
SELECT
    c.course_name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM students s
    WHERE s.course_id = c.id
);

-- Courses without students
SELECT
    c.course_name
FROM courses c
WHERE NOT EXISTS (
    SELECT 1
    FROM students s
    WHERE s.course_id = c.id
);

-- Students who made a payment above average
SELECT
    s.name,
    p.amount
FROM students s
INNER JOIN payments p
ON s.student_id = p.student_id
WHERE p.amount > (
    SELECT AVG(amount)
    FROM payments
);

-- Students whose fee is above their course average
SELECT
    s.name,
    s.fee,
    s.course_id
FROM students s
WHERE s.fee > (
    SELECT AVG(s2.fee)
    FROM students s2
    WHERE s2.course_id = s.course_id
);

This complete example demonstrates scalar subqueries, IN, EXISTS, NOT EXISTS, aggregate functions, and correlated subqueries.

📌 Key Points

  • A subquery is a query inside another SQL query.
  • Subqueries are normally written inside parentheses.
  • A scalar subquery returns one value.
  • IN is useful when a subquery returns multiple values.
  • EXISTS checks whether the subquery returns at least one row.
  • NOT EXISTS checks whether the subquery returns no rows.
  • A correlated subquery references a value from the outer query.
  • Subqueries can be used in SELECT, WHERE, FROM, HAVING, INSERT, UPDATE, and DELETE statements.
  • A subquery in FROM is called a derived table and needs an alias.
  • Subqueries can use aggregate functions such as AVG(), MAX(), MIN(), and COUNT().
  • Be careful when using NOT IN with possible NULL values.
  • Always test a complex subquery separately before using it inside a larger query.
  • Some problems can be solved using either JOINs or subqueries.

🧠 Quick Quiz

Question: What is a subquery?