Advanced SQL — The Patterns You Will Use Every Week20 min read
Module P-1·20 min read
CTEs, window functions, upserts, subqueries, UNION — the SQL that separates proficient engineers from beginners.
P-1 — Advanced SQL: The Patterns You Will Use Every Week
Who this module is for: You completed Phase 1 and can write fundamental SQL. Phase 2 assumes you are building real applications and need the SQL patterns that separate engineers who can write queries from engineers who can write good queries. This module covers the constructs that appear in almost every production PostgreSQL codebase.
Common Table Expressions (CTEs) — WITH Clauses
A CTE creates a named, temporary result set within a query. It makes complex queries readable by breaking them into named steps.
sql
Chaining Multiple CTEs
sql
CTEs execute once and are referenced by name — they are not re-executed for each reference. This is important for performance.
Recursive CTEs — Traversing Hierarchies
Recursive CTEs can traverse tree structures (org charts, categories with subcategories, file systems):
sql
Window Functions — Computation Without Collapsing Rows
Aggregate functions collapse many rows into one. Window functions compute across a set of rows but keep every row in the output.
sql
The OVER() Clause
OVER() defines the "window" — the set of rows each calculation sees:
sql
Essential Window Functions
Ranking:
sql
Difference
When there is a tie
ROW_NUMBER()
Assigns unique numbers (arbitrary tie-breaking)
RANK()
Tied rows get same rank; next rank skips numbers (1,1,3)
DENSE_RANK()
Tied rows get same rank; next rank does not skip (1,1,2)
Access previous/next rows:
sql
Running totals:
sql
Percentile:
sql
Subqueries
A subquery is a query nested inside another query.
Scalar subquery (returns one value)
sql
IN subquery (returns a list)
sql
EXISTS — Efficient Existence Check
EXISTS stops as soon as it finds the first match — more efficient than IN for large tables:
sql
Correlated subquery (references outer query)
sql
INSERT ... ON CONFLICT — Upsert
Upsert inserts a row if it does not exist, updates it if it does. Essential for idempotent writes.
sql
sql
sql
RETURNING with DML — Get Data Back Without a Second Query
RETURNING retrieves data from modified rows in the same statement:
sql
UNION, INTERSECT, EXCEPT
Combine result sets from multiple SELECT statements:
sql
CASE Expressions — Inline Conditionals
sql
Practical Exercise: Task Dashboard Queries
Using the task manager schema from F-7:
sql
Summary
Feature
When to Use
CTE (WITH)
Break complex queries into readable named steps
Recursive CTE
Traverse hierarchical data (org charts, categories)
ROW_NUMBER()
Unique sequential position
RANK() / DENSE_RANK()
Position with tied rows
LAG() / LEAD()
Access previous/next row values
Running SUM() OVER
Cumulative totals
Scalar subquery
Single-value comparison (price > (SELECT AVG...))
EXISTS
Efficient check for row existence
ON CONFLICT DO UPDATE
Upsert — insert or update atomically
ON CONFLICT DO NOTHING
Insert if not exists, ignore if duplicate
RETURNING
Get modified row data back without a second query
FILTER (WHERE ...)
Conditional aggregation without subqueries
CASE WHEN
Inline conditional logic in SELECT
Module P-2 covers indexing from a practitioner's perspective — not the internals (that is Phase 3), but when to add an index, when not to, and how to read EXPLAIN output to measure the difference.
A Senior Software Engineer is optimizing a complex report that uses a Common Table Expression (CTE) named sales_summary multiple times within the final SELECT statement. They are concerned about potential performance bottlenecks due to repeated execution of the sales_summary logic. Based on PostgreSQL's behavior with CTEs, what is the most accurate understanding of how sales_summary will be executed?
A database architect is designing a query to identify all customers who have placed at least one order with a total value exceeding $500. The customers table is very large, and the orders table contains millions of records. Which SQL construct is generally the most performant and idiomatic for this specific existence check in PostgreSQL, and why?
A developer is tasked with calculating the average price of products within each category and displaying this average alongside every product in that category. However, their current query is incorrectly showing the overall average price of *all* products for every row. Which of the following is the most likely cause of this issue in their window function implementation?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
-- Without CTE: hard to readSELECT u.display_name, task_counts.total
FROM users u
JOIN(SELECT assigned_to,COUNT(*)AS total
FROM tasks
WHEREstatus!='done'GROUPBY assigned_to
) task_counts ON u.id = task_counts.assigned_to
ORDERBY task_counts.total DESC;-- With CTE: same query, readableWITH open_task_counts AS(SELECT assigned_to,COUNT(*)AS total
FROM tasks
WHEREstatus!='done'GROUPBY assigned_to
)SELECT u.display_name, otc.total
FROM users u
JOIN open_task_counts otc ON u.id = otc.assigned_to
ORDERBY otc.total DESC;
WITH-- Step 1: get all orders in the last 30 daysrecent_orders AS(SELECT customer_id, product_id, quantity, unit_price
FROM orders
WHERE ordered_at >NOW()-INTERVAL'30 days'),-- Step 2: calculate revenue per customer from those orderscustomer_revenue AS(SELECT customer_id,SUM(quantity * unit_price)AS revenue
FROM recent_orders
GROUPBY customer_id
),-- Step 3: rank customers by revenueranked_customers AS(SELECT customer_id, revenue, RANK()OVER(ORDERBY revenue DESC)AS rank
FROM customer_revenue
)SELECT c.name, rc.revenue, rc.rank
FROM ranked_customers rc
JOIN customers c ON rc.customer_id = c.id
WHERE rc.rank <=10;-- top 10 customers
-- Find all members of a team hierarchy, starting from a given managerWITH RECURSIVE team_hierarchy AS(-- Base case: the starting employeeSELECT id, name, manager_id,0AS depth
FROM employees
WHERE id =3-- start from VP Eng (id=3)UNIONALL-- Recursive case: find direct reports of everyone already in the resultSELECT e.id, e.name, e.manager_id, th.depth +1FROM employees e
JOIN team_hierarchy th ON e.manager_id = th.id
)SELECTREPEAT(' ', depth)|| name AS indented_name,-- indent by depth depth
FROM team_hierarchy
ORDERBY depth, name;-- indented_name | depth-- -----------------+--------- VP Eng | 0-- Engineer 1 | 1-- Engineer 2 | 1-- Junior Dev | 2
-- PARTITION BY: like GROUP BY but doesn't collapse rowsOVER(PARTITIONBY category)-- each row sees its category's rows-- ORDER BY: gives rows an order within the partitionOVER(PARTITIONBY category ORDERBY price DESC)-- No partition (all rows): each row sees the entire result setOVER()
-- Find products priced above the overall averageSELECT name, price
FROM products
WHERE price >(SELECTAVG(price)FROM products);
-- Find customers who have placed at least one orderSELECT name FROM customers
WHERE id IN(SELECTDISTINCT customer_id FROM orders);-- Find customers who have NEVER orderedSELECT name FROM customers
WHERE id NOTIN(SELECTDISTINCT customer_id FROM orders WHERE customer_id ISNOTNULL);
-- Find customers who have at least one high-value orderSELECT c.name
FROM customers c
WHEREEXISTS(SELECT1FROM orders o
WHERE o.customer_id = c.id
AND o.quantity * o.unit_price >200);-- "SELECT 1" — EXISTS only cares whether a row exists, not what it contains
-- Find the most expensive product in each categorySELECT name, price, category
FROM products p1
WHERE price =(SELECTMAX(price)FROM products p2
WHERE p2.category = p1.category -- references the outer query's row);
-- Insert or update a product (based on unique SKU)INSERTINTO products (sku, name, price, in_stock)VALUES('KB-001','Mechanical Keyboard',129.99,true)ON CONFLICT (sku)DOUPDATESET price = EXCLUDED.price, in_stock = EXCLUDED.in_stock, name = EXCLUDED.name;-- EXCLUDED.* refers to the values that were attempted to be inserted
-- Insert or ignore (do nothing if duplicate)INSERTINTO users (email)VALUES('alice@example.com')ON CONFLICT (email)DO NOTHING;-- No error, no update — silently skips the insert if email exists
-- Upsert with conditional update (only update if the new value is higher)INSERTINTO product_stats (product_id, views)VALUES(1,100)ON CONFLICT (product_id)DOUPDATESET views = product_stats.views + EXCLUDED.views;-- Adds new views to existing views on conflict
-- Insert and get the generated ID backINSERTINTO tasks (project_id, created_by, title)VALUES(1,1,'New task')RETURNING id, created_at;-- Update and see what changedUPDATE products
SET price = price *0.9WHERE category ='accessories'RETURNING id, name, price AS new_price;-- Delete and retrieve what was removedDELETEFROM tasks
WHEREstatus='cancelled'AND created_at <NOW()-INTERVAL'30 days'RETURNING id, title, project_id;
-- UNION: all rows from both queries (removes duplicates)SELECT email FROM customers
UNIONSELECT email FROM newsletter_subscribers;-- UNION ALL: all rows including duplicates (faster — no deduplication)SELECT'New'AStype, id, title FROM tasks WHEREstatus='todo'UNIONALLSELECT'Active'AStype, id, title FROM tasks WHEREstatus='in_progress';-- INTERSECT: only rows that appear in bothSELECT email FROM customers
INTERSECTSELECT email FROM newsletter_subscribers;-- Customers who are also subscribers-- EXCEPT: rows in first but not secondSELECT email FROM customers
EXCEPTSELECT email FROM newsletter_subscribers;-- Customers who are NOT subscribers (compare with LEFT JOIN ... WHERE IS NULL)
-- Categorise pricesSELECT name, price,CASEWHEN price <30THEN'budget'WHEN price <100THEN'mid-range'ELSE'premium'ENDAS price_tier
FROM products;-- Count by status using conditional aggregationSELECTCOUNT(*) FILTER (WHEREstatus='todo')AS todo_count,COUNT(*) FILTER (WHEREstatus='in_progress')AS active_count,COUNT(*) FILTER (WHEREstatus='done')AS done_count
FROM tasks
WHERE project_id =1;-- Equivalent with CASE:SELECTSUM(CASEWHENstatus='todo'THEN1ELSE0END)AS todo_count,SUM(CASEWHENstatus='in_progress'THEN1ELSE0END)AS active_count,SUM(CASEWHENstatus='done'THEN1ELSE0END)AS done_count
FROM tasks
WHERE project_id =1;
-- Q1: For each project, show total tasks, done tasks, and % completionWITH task_summary AS(SELECT project_id,COUNT(*)AS total,COUNT(*) FILTER (WHEREstatus='done')AS done,COUNT(*) FILTER (WHEREstatus='in_progress')AS in_progress
FROM tasks
GROUPBY project_id
)SELECT p.name AS project, ts.total, ts.done, ts.in_progress,ROUND(100.0* ts.done /NULLIF(ts.total,0),1)AS pct_complete
FROM projects p
JOIN task_summary ts ON p.id = ts.project_id;-- Q2: Rank users by number of tasks they have completedSELECT u.display_name,COUNT(*)AS completed_tasks, RANK()OVER(ORDERBYCOUNT(*)DESC)AS rank
FROM tasks t
JOIN users u ON t.assigned_to = u.id
WHERE t.status='done'GROUPBY u.id, u.display_name;-- Q3: For each task, show how long it has been open (or was open)SELECT title,status, created_at,COALESCE(completed_at,NOW())AS closed_or_now, AGE(COALESCE(completed_at,NOW()), created_at)AS age
FROM tasks
ORDERBY created_at;-- Q4: Upsert a task status (idempotent status update)INSERTINTO tasks (id, project_id, created_by, title,status)VALUES(1,1,1,'Existing Task','in_progress')ON CONFLICT (id)DOUPDATESETstatus= EXCLUDED.status, updated_at =NOW();-- Q5: Find projects with no tasksSELECT p.name, p.created_at
FROM projects p
WHERENOTEXISTS(SELECT1FROM tasks t WHERE t.project_id = p.id
);