Subqueries in FROM (derived tables) in PostgreSQL - Time & Space Complexity
When we use subqueries inside the FROM clause, the database runs these smaller queries first before the main query.
We want to understand how the total work grows as the data size grows.
Analyze the time complexity of the following code snippet.
SELECT dt.customer_id, dt.total_orders
FROM (
SELECT customer_id, COUNT(*) AS total_orders
FROM orders
GROUP BY customer_id
) AS dt
WHERE dt.total_orders > 5;
This query counts orders per customer in a subquery, then filters customers with more than 5 orders.
- Primary operation: Scanning all rows in the orders table to count orders per customer.
- How many times: Once over all orders; then a quick filter over the grouped results.
The database reads every order once to count them by customer, so if orders grow, work grows too.
| Input Size (n orders) | Approx. Operations |
|---|---|
| 10 | About 10 reads and counts |
| 100 | About 100 reads and counts |
| 1000 | About 1000 reads and counts |
Pattern observation: The work grows roughly in direct proportion to the number of orders.
Time Complexity: O(n)
This means the time to run the query grows roughly in a straight line as the number of orders increases.
[X] Wrong: "The subquery runs multiple times, so the time grows much faster than the data size."
[OK] Correct: The subquery runs once to prepare the data, not repeatedly, so time grows mainly with input size, not squared.
Understanding how subqueries inside FROM work helps you explain query performance clearly and shows you can think about data size effects.
"What if the subquery included a join with another large table? How would the time complexity change?"
Practice
FROM clause in PostgreSQL?Solution
Step 1: Understand the role of subqueries in FROM
Subqueries in the FROM clause act like temporary tables that exist only during the query execution.Step 2: Compare with other options
Updating or deleting data and creating permanent tables are not done by subqueries in FROM.Final Answer:
To create a temporary table that can be used in the main query -> Option DQuick Check:
Subquery in FROM = temporary table [OK]
- Thinking subqueries in FROM create permanent tables
- Confusing subqueries with UPDATE or DELETE statements
- Forgetting subqueries need an alias
FROM clause in PostgreSQL?Solution
Step 1: Identify correct subquery syntax in FROM
The subquery must be enclosed in parentheses and given an alias using AS.Step 2: Check each option
SELECT * FROM (SELECT id FROM users) AS sub; correctly uses parentheses and alias. Others either misuse WHERE, JOIN without ON, or lack alias.Final Answer:
SELECT * FROM (SELECT id FROM users) AS sub; -> Option AQuick Check:
Subquery in FROM needs parentheses + alias [OK]
- Omitting alias after subquery
- Using subquery in WHERE without proper syntax
- Trying to JOIN without ON condition
users(id, name)orders(id, user_id, amount)What will the following query return?
SELECT sub.name, sub.total FROM (SELECT u.name, SUM(o.amount) AS total FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.name) AS sub WHERE sub.total > 100;
Solution
Step 1: Analyze the subquery
The subquery calculates total order amount per user using LEFT JOIN and GROUP BY user name.Step 2: Apply the outer WHERE filter
The outer query filters users whose total order amount is greater than 100.Final Answer:
Names of users with total order amount greater than 100 -> Option CQuick Check:
Subquery sums orders, outer filters total > 100 [OK]
- Ignoring GROUP BY causing syntax error
- Confusing LEFT JOIN with INNER JOIN results
- Assuming all users appear without filtering
SELECT sub.name, sub.order_count FROM (SELECT name, COUNT(*) FROM users GROUP BY name) sub;
Solution
Step 1: Check subquery SELECT list
COUNT(*) needs an alias to be referenced outside the subquery.Step 2: Understand outer query references
Outer query uses sub.order_count, but COUNT(*) column has no alias, causing error.Final Answer:
Missing alias for COUNT(*) column -> Option BQuick Check:
Aggregate columns need aliases in subqueries [OK]
- Forgetting to alias aggregate functions
- Assuming subquery columns auto-named
- Ignoring parentheses around subquery
FROM clause to achieve this?Solution
Step 1: Understand the requirement
We need average order amount per user but only for users with more than 3 orders.Step 2: Analyze each option
SELECT sub.user_id, sub.avg_amount FROM (SELECT user_id, AVG(amount) AS avg_amount, COUNT(*) AS order_count FROM orders GROUP BY user_id) AS sub WHERE sub.order_count > 3; uses a subquery to calculate average and count per user, then filters users with order_count > 3 outside. Others misuse COUNT in WHERE or HAVING incorrectly.Final Answer:
SELECT sub.user_id, sub.avg_amount FROM (SELECT user_id, AVG(amount) AS avg_amount, COUNT(*) AS order_count FROM orders GROUP BY user_id) AS sub WHERE sub.order_count > 3; -> Option AQuick Check:
Subquery calculates aggregates; outer filters by count [OK]
- Using COUNT(*) in WHERE clause directly
- Misplacing HAVING clause conditions
- Not aliasing subquery columns properly
Ready to prove it?
Pass the test and this pattern moves unknown
