MySQL
ORACLE
POSTGRESQL
SQL
SQL SERVER

The Complete Guide to SQL Subqueries

intro

Let’s master SQL subqueries. Reduce the number of queries you perform and start embedding advanced query logic into single SQL queries.

Tools used in the tutorial
Tool Description Link
Dbvisualizer DBVISUALIZER
TOP RATED DATABASE MANAGEMENT TOOL AND SQL CLIENT

To perform many tasks, you may need to launch several queries or use queries with many JOINs. In the first case, you might prefer to operate at the application level to get the desired result. In the second, you may have to write long queries that are difficult to read and maintain. Luckily, SQL subqueries exist!

An SQL subquery allows you to execute a query within the scope of another query. This means that subqueries do not involve any of the drawbacks mentioned above.

Let’s now dig into subqueries in SQL. At the end of this blog post, you will be a subquery SQL master!

What Is a Subquery in SQL?

An SQL subquery is a query that is nested inside another query. SQL subqueries are also called “nested queries” or “inner queries,” while the SQL statement containing the subquery is typically referred to as an “outer query.”

Generally, a subquery in SQL is used to retrieve data that will be used in the outer query. In this scenario, the inner query is executed first. Then, its results are used to evaluate the outer query. However, keep in mind that the order of execution of the inner query and the outer query depends on the specific scenario.

Typically, you can use SQL subqueries in SELECT, INSERT, UPDATE, and DELETE statements. Specifically, you can nest a subquery in the SELECT, FROM, WHERE, JOIN, and HAVING SQL clauses. Also, you can adopt SQL queries in conjunction with several SQL operators, such as =, <, >, >=, <=, IN, NOT IN, EXISTS, NOT EXISTS, and more.

For more details, follow these links from the official documentations:

SQL Subquery Example

This is what an query that involves an SQL subquery looks like:

Copy
        
1 -- selecting the list of users 2 -- with the longest nicknames 3 SELECT id, nickname 4 FROM users 5 WHERE LENGTH(nickname) > ( 6 SELECT AVG(LENGTH(nickname)) 7 FROM users 8 );

Here, the subquery is the following query embedded between parentheses:

Copy
        
1 SELECT AVG(LENGTH(nickname)) 2 FROM users;

Time to discover the benefits of using SQL subqueries.

Why Use SQL Subqueries?

Subqueries are particularly useful because they allow you to embed specific query logic into a more general query. Thus, by running a single query, you can get results that would naturally require multiple queries. That can lead to benefits in terms of readability, maintainability, and even performance.

Let’s better understand why SQL subqueries are important with an example. Assume you want to find all the users with above-average points. You may be tempted to write the following SQL query:

Copy
        
1 SELECT id, nickname 2 FROM users 3 WHERE points > AVG(points)

But keep in mind that you cannot use aggregate operators inside the WHERE clause. So, the above query will result in an error.

Instead, what you need to do is:

  1. Find the average number of points
  2. Select all users whose points are greater than the average number

First, launch the following query:

Copy
        
1 -- getting the average number of points 2 SELECT AVG(points) 3 FROM users;

Now, for this example, suppose the result is 420.

Then, you can achieve the end goal with the query below:

Copy
        
1 SELECT id, nickname 2 FROM users 3 WHERE points > 420;

As you can see, finding the list of users with above-average points would require two queries. This also means that if you want to perform this operation programmatically, you would have to save the result of the first query in a variable at the application level, and then use it in the WHERE clause of the second.

Otherwise, you can simply achieve the same result with a single query involving a subquery:

Copy
        
1 SELECT id, nickname 2 FROM users 3 WHERE points > ( 4 SELECT AVG(points) 5 FROM users 6 );
Example of a single query involving an SQL subquery in DbVisualizer
Example of a single query involving an SQL subquery in DbVisualizer

What happens here is that:

  1. The DBMS engine will execute this subquery SQL statement first:
Copy
        
1 SELECT AVG(points) 2 FROM users;
  1. Then, it will replace the SQL subquery with its result and execute the outer query accordingly.

In other terms, this logic corresponds exactly to the two queries above, but in a single—more readable—query. This was just a simple example to understand how powerful SQL subqueries are.

Now, remember that a subquery in SQL can come in handy in many other situations. Learn more about how you can use a subquery in SQL.

When to Use a Subquery in SQL

SQL subqueries let you nest queries within other queries, making data retrieval more dynamic and efficient. These make them ideal for the following scenarios:

  • Filtering data: Use subqueries in WHERE or HAVING clauses to filter results based on another query.
  • Aggregations on groups: Compute summary statistics with aggregate functions (e.g., AVG(), MIN(), etc.) within a subquery and use them in the main query.
  • Joins alternative: When a JOIN would complicate the query, you might be able to a subquery to get the same results.
  • Checking existence: Use EXISTS or NOT EXISTS with subqueries to verify data presence.
  • Generating derived tables: Use subqueries in the FROM clause to create derived tables.

How to Use Subqueries in SQL

When it comes to subqueries, there are a few rules to consider:

  1. A subquery in SQL must be enclosed within round brackets.
  2. Many DBMSs, such as Oracle, MySQL or SQL Server, may require giving SQL subqueries an alias.
  3. You cannot use a subquery in the ORDER BY clause.
  4. The BETWEEN operator does not support subqueries.
  5. You can use a SQL subquery that returns multiple rows only with multiple value SQL operators or aggregate functions, such as IN, AVG(), and MAX().

These rules define how you can use a subquery in SQL. Let’s now look at some examples to understand the syntax required by SQL subqueries.

Subqueries in CRUD Operations

CRUD stands for “Create, Read, Update, and Delete” and includes the four most common SQL statements you generally perform in a database:

  • INSERT: Corresponds to the “Create” operation
  • SELECT: Corresponds to the “Read” operation
  • UPDATE: Corresponds to the “Update” operation
  • DELETE: Corresponds to the “Delete” operation

See how to use SQL subqueries in these CRUD statements.

Subqueries in an INSERT Clause

You can use a subquery in the WHERE clause of an INSERT statement to select data from one or more tables and insert them into a new table.

Syntax:

Copy
        
1 INSERT INTO new_table_name [ (column1 [, column2 ]) ] 2 SELECT [ * | (column1 [, column2 ]) ] 3 FROM existing_table 4 [ WHERE ... [ (SELECT ...) ] ]

Example:

Copy
        
1 INSERT INTO top_users_copy 2 SELECT * FROM users 3 WHERE id IN ( 4 SELECT id 5 FROM users 6 WHERE points > 200 7 );

This query will copy all users with more than 200 points into the best_users_copy new table. To do so, it selects the users with more than 200 points with an SQL subquery.

Executing the INSERT query with a subquery in DbVisualizer
Executing the INSERT query with a subquery in DbVisualizer

Subqueries in a SELECT Clause

SQL subqueries are allowed in the SELECT, FROM, WHERE, JOIN, and HAVING SQL clauses of a SELECT query.

Syntax:

Copy
        
1 SELECT column_1 [, column_2 ] [, (SELECT ...) ] 2 FROM exisisting_table 3 [ JOIN [ (SELECT ...) ] ON ... ] 4 [ WHERE ... [ (SELECT ...) ] ] 5 [ 6 GROUP BY column_1 [, column_2 ] 7 HAVING ... [ (SELECT ...) ] ] 8 ]

Example:

Copy
        
1 SELECT 2 id, 3 nickname, 4 points, 5 (SELECT AVG(points) FROM users) AS avg_points 6 FROM users;

The above query returns the list of users, with a special field representing the number of average points in each row. This field, avg_points is populated on the fly using the subquery SQL mechanism.

Executing the SELECT query with a subquery in DbVisualizer
Executing the SELECT query with a subquery in DbVisualizer

Subqueries in an UPDATE Clause

You can adopt SQL subqueries in the WHERE clause of an UPDATE statement to select the data set that needs to be updated.

Syntax:

Copy
        
1 UPDATE existing_table 2 SET column_name = new_value 3 [ WHERE ... [ (SELECT ...) ] ]

Example:

Copy
        
1 UPDATE users 2 SET points = points * 0.5 3 WHERE id IN ( 4 SELECT id FROM top_users_copy 5 );

This query reduces the points of top users in users by half. To do so, it retrieves the IDs of the top users with a subquery.

Executing the UPDATE query with a subquery in DbVisualizer
Executing the UPDATE query with a subquery in DbVisualizer

Subqueries in a DELETE Clause

You can employ one or more SQL subqueries in the WHERE clause of a DELETE SQL statement to select the data to delete.

Syntax:

Copy
        
1 DELETE FROM existing_table 2 [ WHERE ... [ (SELECT ...) ] ]

Example:

Copy
        
1 DELETE FROM users 2 WHERE id IN ( 3 SELECT id FROM top_users_copy 4 );

This query deletes all top users read from top_users_copy in the users table. Again, the top users are retrieved using a subquery.

Executing the DELETE query with a subquery in DbVisualizer
Executing the DELETE query with a subquery in DbVisualizer

How to Handle Long Queries Relying on the SQL Subquery Mechanism

SQL subqueries are great for compartmentalizing specific sections of business logic in data selection. However, they can make a query long and complex. To manage lengthy queries with one or more subqueries, the best approach is to use an SQL formatter.

Ideally, your database client should offer this capability, such as DbVisualizer, which lets you format SQL queries with just a few clicks.

For example, suppose you have the following long and messy query with multiple subqueries:

Copy
        
1 SELECT e.employee_id, e.employee_name, (SELECT d.department_name FROM departments d WHERE d.department_id = e.department_id) AS department, (SELECT AVG(s.salary) 2 FROM salaries s WHERE s.department_id = e.department_id) AS avg_department_salary 3 FROM employees e 4 WHERE e.salary > (SELECT AVG(salary) FROM employees);

To format it in DbVisualizer, simply paste the query, right-click on it, and select the "Format SQL > Format Buffer" option.

Selecting the Format SQL > Format Buffer option
Selecting the “Format SQL > Format Buffer” option

Selecting the “Format SQL > Format Buffer” option

This will automatically format the SQL subquery statement for you, producing a much more readable result:

The formatted SQL query
The formatted SQL query

The resulting SQL query will be:

Copy
        
1 SELECT 2 e.employee_id, 3 e.employee_name, 4 ( SELECT 5 d.department_name 6 FROM 7 departments d 8 WHERE 9 d.department_id = e.department_id 10 ) AS department, 11 ( SELECT 12 AVG(s.salary) 13 FROM 14 salaries s 15 WHERE 16 s.department_id = e.department_id 17 ) AS avg_department_salary 18 FROM 19 employees e 20 WHERE 21 e.salary > 22 ( SELECT 23 AVG(salary) 24 FROM 25 employees 26 );

Much better, right?

Note: The query formatting feature is only available in DbVisualizer Pro. You can test it with a 21-day free trial.

Conclusion

In this blog post, you learned what an SQL subquery is, when to use it, and how to apply it effectively in SQL. You also explored real-world subquery SQL examples involving CRUD operations.

SQL subqueries are a powerful tool for enhancing your queries, enabling you to perform multiple operations simultaneously. Yet, since subqueries impact both performance and readability, it is important to use them wisely.

As demonstrated, using a database client with built-in SQL subquery support and formatting features, like DbVisualizer, can help you manage subqueries more efficiently and avoid unnecessary complexity. Try DbVisualizer for free!

FAQ

What is a correlated subquery in SQL?

A correlated subquery is an SQL subquery that depends on values from the outer. Since the DBMS engine may have to execute the subquery once for each row processed by the outer query, queries involving correlated subqueries can be very slow.

This is an example of a correlated subquery:

Copy
        
1 SELECT id, nickname, points, (SELECT AVG(points) 2 FROM users 3 WHERE role = U.role) AS avg_role_points 4 FROM users U

This query returns the list of users. For each user, there is an extra column containing the average number of points of users with their same role.

Can you use 2 subqueries in an SQL query?

Yes, an outer SQL query can involve an arbitrary number of subqueries. There is no limit to the number of subqueries you can use in an SQL query.

What are the types of subqueries?

There are several types of subqueries. The most important ones are:

  • Single-row subquery
  • Multi-row subquery
  • Single-column subquery
  • Multi-column subquery
  • Correlated subquery

What is an example of an SQL JOIN subquery?

You can write an SQL JOIN subquery as in the example below:

Copy
        
1 SELECT U1.id, U1.nickname 2 FROM 3 ( 4 SELECT id, points 5 FROM users 6 WHERE points > 200 7 ) AS U1 8 JOIN 9 ( 10 SELECT user_id 11 FROM subscriptions 12 WHERE deleted_by IS NOT NULL 13 ) AS U2 on U1.id = U2.user_id

This query joins all users with more than 200 points with their subscriptions that have not been deleted yet.

Are subqueries faster than JOINs?

Usually, JOINs are faster than subqueries. This is because most RDBMS technologies can create a better execution plan when it comes to JOIN queries. At the same time, it depends a lot on the DBMS version and the specific case, so it is hard to say. What is for sure is that JOINs and subqueries enable you to get the same result in several scenarios. The advantage of subqueries is that they generally lead to a more readable query than JOINs. So, if performance is not too important, subqueries may be preferable.

What is an example of an Oracle SQL subquery?

In Oracle, subqueries follow the same syntax discussed earlier. So, you can use an Oracle SQL subquery as shown below:

Copy
        
1 SELECT employee_id, name 2 FROM employees 3 WHERE department_id = (SELECT department_id FROM departments WHERE name = 'Sales')

This query retrieves employees who belong to the "Sales" department. The subquery first finds the department_id for "Sales," and the main query selects employees matching that ID.

What is the difference between an SQL subquery and a derived table?

An SQL subquery is a query nested within another query, often used to return a single value or a set of values. On the other hand, a derived table, is a type of subquery that generates a temporary table or result set in the scope of a FROM clause. Unlike regular subqueries, derived tables are explicitly given an alias and are typically used only to provide a result set for the outer query to operate on.

Dbvis download link img
About the author
Antonello Zanini

Antonello is a software engineer, and often refers to himself as a technology bishop. His mission is to spread knowledge through writing.

The Table Icon
Sign up to receive The Table's roundup
More from the table
Title Author Tags Length Published
title

Check If A Table Exists in SQL: Multiple Approaches

author Antonello Zanini tags MySQL POSTGRESQL SQL SQL SERVER 6 min 2025-05-14
title

SQL: Add a Primary Key to an Existing Table

author TheTable tags ALTER TABLE SQL 5 min 2025-05-13
title

A Complete Guide to NOT EXISTS in SQL

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 8 min 2025-05-12
title

Text Extraction Made Easy With SUBSTRING in PostgreSQL

author TheTable tags POSTGRESQL 5 min 2025-05-07
title

Standard ANSI SQL: What It Is and Why It Matters

author Antonello Zanini tags SQL 8 min 2025-05-06
title

SQL DROP TABLE IF EXISTS Statement: Complete Guide

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2025-05-05
title

CREATE DATABASE in PostgreSQL: A Complete Guide

author Antonello Zanini tags POSTGRESQL 6 min 2025-04-30
title

Time-Tested Ways on How to Prevent SQL Injection Attacks

author Lukas Vileikis tags SQL 9 min 2025-04-29
title

A Beginner's Guide to Vector Search Using pgvector

author Lukas Vileikis tags POSTGRESQL Vectors 3 min 2025-04-24
title

TRUNCATE vs DELETE: SQL Comparison of the Two Statements

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 9 min 2025-04-23

The content provided on dbvis.com/thetable, including but not limited to code and examples, is intended for educational and informational purposes only. We do not make any warranties or representations of any kind. Read more here.