MySQL SubQuery with Examples

โšก Smart Summary

MySQL SubQuery syntax places one SELECT statement inside another, so the inner result feeds the outer query. This explanation covers scalar, row, and table subqueries, execution order, practical examples, and the performance trade-off against JOIN operations.

  • ๐Ÿ” Core Definition: A subquery is a SELECT statement nested inside another query, and the inner query runs first to supply values to the outer query.
  • ๐Ÿงฎ Scalar Subquery: Returns a single row and a single column, so it pairs with comparison operators such as equals, greater than, or less than.
  • ๐Ÿ“‹ Row and Table Subqueries: A row subquery returns one row with several columns, while a table subquery returns many rows and works with the IN operator.
  • ๐Ÿงฉ Nesting Depth: Subqueries can be nested several levels deep, which locates values such as the highest paying member in one statement.
  • โœ๏ธ Beyond SELECT: INSERT, UPDATE, and DELETE statements accept subqueries, which makes bulk changes possible without temporary tables.
  • โšก Performance Rule: A JOIN usually runs far faster than an equivalent subquery, so reserve subqueries for logic that a JOIN cannot express.

MySQL SubQuery

What is a SubQuery in SQL?

A subquery is a SELECT query that is contained inside another query. The inner select query is usually used to determine the results of the outer select query, so the database evaluates the inner statement first and then passes its output upwards.

The inner query is called the inner query or nested query, and the statement that contains it is called the outer query. Let us look into the sub query syntax.

MySQL SubQuery

The diagram above shows the general shape of the statement: the outer SELECT supplies the columns you want to see, and the bracketed inner SELECT supplies the value or the list of values that the WHERE clause compares against.

Why Use a SubQuery?

Before looking at the different types, it helps to know when a subquery earns its place in a statement.

A subquery answers a question whose filter value is not known in advance. It has to be calculated from the data itself. A common customer complaint at the MyFlix Video Library is the low number of movie titles, and the management wants to buy movies for the category which has the least number of titles. Nobody knows which category that is until the database is asked, so the value must be computed first and then used as a filter.

Subqueries are attractive for three practical reasons:

  • Readability: Each part of the logic sits in its own bracketed block, so the statement reads as a sequence of small questions rather than one complex expression.
  • Isolation: An inner query can be run on its own to confirm that it returns the expected value, which makes testing and debugging far easier.
  • Flexibility: The same pattern works in the WHERE, HAVING, SELECT, and FROM clauses, and also inside INSERT, UPDATE, and DELETE statements.

The trade-off is speed, which is examined in the JOIN comparison later in this article.

Types of SubQueries in MySQL

MySQL supports three types of subqueries, and the type is decided by the shape of the result that the inner query returns. Each type is explained below with a working example against the myflixdb database.

1) Scalar SubQuery

A scalar subquery returns exactly one row and one column, which means it returns a single value. Because the result is a single value, it can be used anywhere a literal value is allowed. Returning to the MyFlix problem above, you can use a query like this one:

SELECT category_name FROM categories
WHERE category_id = (SELECT MIN(category_id) FROM movies);

It gives a result:

MySQL SubQuery

Let us see how this query works.

MySQL SubQuery

As the execution diagram shows, MySQL first runs SELECT MIN(category_id) FROM movies, receives one value, and only then runs the outer query with that value in place. Because a single value is returned, the permissible operators are the standard comparison set: =, <> (or !=), >, >=, <, and <=.

๐Ÿ’ก Tip: If a subquery placed after = returns more than one row, MySQL raises error 1242, Subquery returns more than 1 row. Switch the operator to IN, or tighten the inner WHERE clause.

2) Row SubQuery

A row subquery also returns a single row, but that row may carry more than one column. The outer query therefore compares a row of values against a row constructor rather than against a single value.

SELECT full_names, contact_number FROM members
WHERE (membership_number, gender) = (SELECT membership_number, gender FROM members WHERE full_names = 'Janet Jones');

The permissible operators are the same comparison operators listed above, applied to the whole row at once.

3) Table SubQuery

A table subquery returns multiple rows, and often multiple columns, so the outer query must use a set operator such as IN, NOT IN, ANY, ALL, or EXISTS.

Suppose you want names and phone numbers of members who have rented a movie and are yet to return it, so that you can call them up to give a reminder. You can use a query like this one:

SELECT full_names, contact_number FROM members
WHERE membership_number IN (SELECT membership_number FROM movierentals WHERE return_date IS NULL);

MySQL SubQuery

Let us see how this query works.

MySQL SubQuery

In this case, the inner query returns more than one result, so the list of membership numbers is handed to the IN operator and every matching member is returned.

Nesting SubQueries Several Levels Deep

So far you have seen two levels. A subquery may also contain another subquery, which produces a triple nested statement.

Suppose the management wants to reward the highest paying member. We can run a query like this one:

SELECT full_names FROM members
WHERE membership_number = (SELECT membership_number FROM payments
    WHERE amount_paid = (SELECT MAX(amount_paid) FROM payments));

The innermost query finds the largest payment, the middle query converts that amount into a membership number, and the outer query converts the membership number into a name. The above query gives the following result:

MySQL SubQuery

How to Use SubQueries with INSERT, UPDATE, and DELETE

Subqueries are not limited to SELECT statements. The same bracketed pattern works inside data modification statements, which allows a whole set of rows to be changed in one pass without creating a temporary table.

INSERT with a subquery. A subquery can supply the rows that are inserted, which copies data from one table into another. The column list of the SELECT must line up with the column list of the INSERT.

INSERT INTO vip_members (membership_number, full_names)
SELECT membership_number, full_names FROM members
WHERE membership_number IN (SELECT membership_number FROM payments WHERE amount_paid > 5000);

UPDATE with a subquery. Here the inner query decides which rows are touched. The example below flags every member whose rental is still outstanding.

UPDATE members
SET reminder_sent = 1
WHERE membership_number IN (SELECT membership_number FROM movierentals WHERE return_date IS NULL);

DELETE with a subquery. The same idea removes rows that satisfy a condition held in a second table.

DELETE FROM members
WHERE membership_number NOT IN (SELECT membership_number FROM movierentals);

โš ๏ธ Warning: MySQL does not allow a statement to modify a table and select from the same table inside a subquery in the FROM clause. If error 1093 appears, wrap the inner query in a derived table, for example SELECT * FROM (SELECT ...) AS t, so that MySQL materialises the result before the change is applied. It is also wise to run the inner SELECT on its own first, and to confirm the row count, before running an UPDATE or a DELETE in production.

SubQueries vs Joins

Both a subquery and a JOIN can combine information from more than one table, so the natural question is which one to reach for.

When compared with joins, sub-queries are simple to use and easy to read. They are not as complicated as Joins, and hence they are frequently used by SQL beginners.

But sub-queries have performance issues. Using a join instead of a sub-query can at times give you up to 500 times performance boost, because the optimiser can resolve a join in a single pass instead of evaluating the inner statement repeatedly.

Point of comparison SubQuery JOIN
Readability High, as each block answers one question Lower, as all tables appear in one clause
Performance Slower, the inner query may run for every outer row Faster, often by a very large margin
Result columns Only columns of the outer table are returned Columns from every joined table can be returned
Typical use Filtering on a value that must be calculated first Combining related rows from two or more tables
Learning curve Gentle, familiar to beginners Steeper, requires knowledge of join types

Given a choice, it is recommended to use a JOIN over a sub query. Sub-queries should only be used as a fallback solution when you cannot use a JOIN operation to achieve the above.

Sub-Queries Vs Joins

Subqueries are also easy to break down into single logical components, which is very useful when testing and debugging the queries.

FAQs

A correlated subquery refers to a column of the outer query, so it is evaluated once for every outer row. A non-correlated subquery is independent and runs only once. Correlated subqueries are powerful but noticeably slower on large tables.

A subquery can sit in the WHERE clause, the HAVING clause, the SELECT list, or the FROM clause, where it becomes a derived table and requires an alias. It is also valid inside INSERT, UPDATE, and DELETE statements.

Often, yes. AI assistants built into editors such as MySQL Workbench can propose an equivalent JOIN. Always compare row counts and read the EXPLAIN plan before trusting the rewrite, because NULL handling can differ.

Yes. Text to SQL assistants turn a question such as “which category has the fewest movies” into a nested SELECT. Accuracy depends on the schema supplied to the model, so review the generated statement against the real table names.

The error appears when a subquery placed after a comparison operator returns several rows. Replace the operator with IN, ANY, or EXISTS, or tighten the inner WHERE clause so that only one row is returned.

Summarize this post with: