Skip to main content

What is Subquery?

Editorial

Introduction

A subquery is one of the most powerful tools in SQL. It allows you to nest one query inside another, enabling complex data retrieval that would otherwise require multiple separate queries.

In this tutorial, you will learn:

  • The formal definition of a subquery
  • Basic syntax and structure
  • How subqueries relate to outer queries
  • Common terminology and naming conventions

Definition

What is a Subquery?

A subquery (also called an inner query or nested query) is a SQL query embedded inside another SQL query.

Think of it as a query within a query – the inner query runs first, and its result is used by the outer query.

Simple Analogy

Imagine you're asking a question that requires two steps:

  1. Question: "What is the average price?"
  2. Question: "Show me all products above that average price."

A subquery lets you combine these into a single question:

"Show me all products above the average price."

The database handles both steps for you.

Visualization

Diagram showing a subquery as an inner query nested inside an outer query, with the inner result feeding into the outer query

What is a Subquery? -- Nested Query Execution

Basic Syntax

General Structure

SELECT column1, column2
FROM table_name
WHERE column_name operator (
    SELECT column
    FROM another_table
    WHERE condition
);

Key Points

  1. Parentheses are required – The subquery must be enclosed in ( )
  2. The subquery runs first – Its result is available to the outer query
  3. The outer query uses the result – As a value, list, or table

Example: Basic Subquery

SELECT order_id, payment_value
FROM olist_order_payments_dataset
WHERE payment_value > (
    SELECT AVG(payment_value)
    FROM olist_order_payments_dataset
);

How it works:

  1. Inner query calculates: AVG(payment_value) → returns ~154.10
  2. Outer query becomes: WHERE payment_value > 154.10
  3. Final result: All orders with payment above average

Terminology

Common Terms

TermDefinitionExample
SubqueryThe nested (inner) query(SELECT AVG(price) FROM products)
Outer QueryThe main query containing the subquerySELECT * FROM products WHERE price > ...
Inner QuerySame as subquery-
Nested QuerySame as subquery-
Parent QuerySame as outer query-

Subquery Locations

Subqueries can appear in several places:

-- In WHERE clause
WHERE column > (subquery)

-- In FROM clause (derived table)
FROM (subquery) AS alias

-- In SELECT clause
SELECT column, (subquery) AS calculated_value

-- In JOIN clause
JOIN (subquery) AS alias ON ...

-- In HAVING clause
HAVING COUNT(*) > (subquery)

Subquery vs Main Query

Execution Order

Unlike regular SQL clauses (which have a specific execution order), subqueries follow a logical dependency:

1. Subquery executes FIRST
   └── Returns a result (value, list, or table)

2. Outer query executes SECOND
   └── Uses the subquery result

Independence

A subquery is a complete, valid SQL statement on its own. You can run it separately:

-- This subquery works independently:
SELECT AVG(payment_value)
FROM olist_order_payments_dataset;

-- Result: 154.10

Dependency

While subqueries are syntactically independent, their meaning depends on the outer query's context:

-- Subquery asks: What is the average?
-- Outer query asks: Which orders are above that average?

SELECT * FROM olist_order_payments_dataset
WHERE payment_value > (
    SELECT AVG(payment_value) FROM olist_order_payments_dataset
);

Together, they answer a more complex question.

Types of Subqueries (Overview)

By Return Type

TypeReturnsExample Use
ScalarSingle valueWHERE price > (subquery)
RowSingle row, multiple columnsWHERE (col1, col2) = (subquery)
TableMultiple rows and columnsFROM (subquery) AS t
ColumnMultiple rows, single columnWHERE id IN (subquery)

By Relationship to Outer Query

TypeDescription
Non-correlatedIndependent of outer query; runs once
CorrelatedReferences outer query; runs for each row

By Location

LocationCommon Name
FROM clauseDerived table, inline view
WHERE clauseFilter subquery
SELECT clauseScalar subquery
JOIN clauseSubquery join

Each type will be covered in detail in upcoming tutorials.

Practice

Let's practice identifying and writing basic subqueries.

Exercise 1: Identify the Subquery

Run this query and observe how the subquery provides a single value for comparison:

-- Find orders with payment above average
-- The subquery calculates the average payment

SELECT 
    order_id,
    payment_type,
    payment_value
FROM olist_order_payments_dataset
WHERE payment_value > (
    SELECT AVG(payment_value)
    FROM olist_order_payments_dataset
)
ORDER BY payment_value DESC
LIMIT 10;

Exercise 2: Run the Subquery Alone

A subquery is a valid query on its own. Let's see what the average payment value is:

-- Run the subquery independently
-- This shows the value being used for comparison

SELECT AVG(payment_value) AS average_payment
FROM olist_order_payments_dataset;

Exercise 3: Subquery with COUNT

Find customers from cities that have more customers than the average city:

-- Find cities with above-average customer count
-- Step 1: Calculate average customers per city
-- Step 2: Find cities exceeding that average

SELECT 
    customer_city,
    COUNT(*) AS customer_count
FROM olist_customers_dataset
GROUP BY customer_city
HAVING COUNT(*) > (
    SELECT AVG(city_count)
    FROM (
        SELECT COUNT(*) AS city_count
        FROM olist_customers_dataset
        GROUP BY customer_city
    )
)
ORDER BY customer_count DESC
LIMIT 10;

Common Mistakes

Mistake 1: Missing Parentheses

❌ Wrong:

SELECT * FROM orders
WHERE amount > SELECT AVG(amount) FROM orders;

✅ Correct:

SELECT * FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);

Mistake 2: Subquery Returns Multiple Values When One Expected

❌ Wrong:

SELECT * FROM orders
WHERE customer_id = (
    SELECT customer_id FROM customers  -- Returns MANY values!
);

✅ Correct (use IN for multiple values):

SELECT * FROM orders
WHERE customer_id IN (
    SELECT customer_id FROM customers
);

Mistake 3: Forgetting Alias for FROM Subquery

❌ Wrong:

SELECT * FROM (
    SELECT customer_city, COUNT(*) AS cnt
    FROM customers
    GROUP BY customer_city
);  -- Missing alias!

✅ Correct:

SELECT * FROM (
    SELECT customer_city, COUNT(*) AS cnt
    FROM customers
    GROUP BY customer_city
) AS city_counts;  -- Alias required!

Summary

Key Takeaways

  1. A subquery is a query nested inside another query

  2. Also known as: inner query, nested query

  3. Syntax: Must be enclosed in parentheses ( )

  4. Execution order: Subquery runs first, result used by outer query

  5. Independence: A subquery is a valid SQL statement on its own

  6. Locations: Can appear in WHERE, FROM, SELECT, JOIN, and HAVING clauses

  7. Return types: Can return a single value, row, column, or full table

Quick Reference

-- Basic structure
SELECT columns
FROM table
WHERE column operator (subquery);`

`-- Example
SELECT * FROM payments
WHERE amount > (SELECT AVG(amount) FROM payments);

What's Next?

Now that you know what a subquery is, let's explore why you would use subqueries and the benefits they provide over alternative approaches.