SQL, or "Sequel" as some call it, seems super easy at first. You just type something like SELECT * FROM
and suddenly you're looking at data from a database. Pretty cool, right? But SQL can do so much more than just simple stuff. There are powerful tricks hidden in there, and one of the best is called Common Table Expressions, or CTEs for short.
CTEs are a big deal. They can help you with all sorts of things, like making dashboards, running fancy reports, or even solving tricky problems that repeat themselves. In this article, I'm going to explain what CTEs are, how they work, when they're most useful, and show you some real-world use cases. We'll be using PostgreSQL, which is a fantastic and free database system.
I'll also share with you a small project I made myself to explore advanced SQL concepts.
π What is a CTE?
A Common Table Expression (CTE) is a temporary result set that you can reference within a SELECT
, INSERT
, UPDATE
, or DELETE
statement. Think of it like a named subquery that enhances readability and maintainability.
π§ Basic Syntax
WITH cte_name AS (
SELECT column1, column2
FROM your_table
WHERE condition
)
SELECT * FROM cte_name;
PostgreSQL evaluates the CTE first, then uses the result in the main query.
π‘ Real-World Use Cases of CTEs
1. Simplifying Complex Joins & Filters
Nested subqueries can become unreadable. CTEs help you break them into manageable parts.
π Example:
WITH recent_orders AS (
SELECT * FROM orders WHERE order_date > CURRENT_DATE - INTERVAL '30 days'
),
high_value_orders AS (
SELECT * FROM recent_orders WHERE total_amount > 500
)
SELECT customer_id, COUNT(*)
FROM high_value_orders
GROUP BY customer_id;
π‘ Use case: A sales dashboard needs to show active high-value customers in the last 30 days.
2. Recursive Queries
CTEs support recursion β great for working with hierarchical or graph-like data (e.g., org charts, category trees).
π Example: Finding management hierarchy
WITH RECURSIVE org_chart AS (
SELECT id, name, manager_id FROM employees WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id
FROM employees e
INNER JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT * FROM org_chart;
π‘ Use case: Performing a recursive query of all employees under a given manager.
3. Updating with Joins (a CTE hack)
CTEs allow you to "stage" complex data before it is updated or deleted.
π Example:
WITH flagged_users AS (
SELECT user_id FROM login_attempts
GROUP BY user_id
HAVING COUNT(*) > 10
)
UPDATE users
SET is_locked = TRUE
WHERE id IN (SELECT user_id FROM flagged_users);
π‘ Use case: Lock accounts that exceed failed login limits.
π Rules, Restrictions & Tips
β Rules
- CTEs are declared using
WITH
, followed by one or more comma-separated blocks. - You can use CTEs in
SELECT
,INSERT
,UPDATE
, andDELETE
. - Recursive CTEs must be explicitly marked with
WITH RECURSIVE
.
β οΈ Restrictions & Performance Tips
-
PostgreSQL materializes CTEs by default. This means that the results of the CTE query are stored in a temporary structure, often in memory, and then used in the outer query.
- β Why it matters: In performance-critical queries, this can cause slower execution than expected.
- β
Fix: From PostgreSQL 12+, you can use
MATERIALIZED
orNOT MATERIALIZED
to control this behavior. π Example:
WITH cte_name AS NOT MATERIALIZED (
SELECT * FROM large_table WHERE condition
)
SELECT * FROM cte_name;
- Use CTEs wisely. Sometimes replacing a CTE with an inline subquery improves speed.
π RDBMS Support
Feature | PostgreSQL | MySQL | SQL Server | Oracle |
---|---|---|---|---|
Basic CTE | β | β (v8.0+) | β | β |
Recursive CTE | β | β (v8.0+) | β | β |
Materialized CTE | β (v12+) | β | β | β |
β PostgreSQL stands out for flexibility and control over CTE behavior.
π₯οΈ My SQL and PostgreSQL Learning Project
Title: Online Learning Platform (SQL-Only Backend)
For real-world knowledge with PostgreSQL, I created a backend system for an online learning platform that was totally written in SQL and had no frameworks, ORMs, or external tools.
The platform mimics real-world systems like Coursera or Udemy with practice like:
- Schema Design
- Query Practice
- 10 Beginner-level queries
- 10 Intermediate queries
- Advanced queries using:
- CTEs
- Transactions
- Window functions
- Triggers and functions
- Access Control & Security
- Role-based access
- PostgreSQL roles & permissions
- Row-Level Security (RLS) setup
- Views
- Logical abstraction with views
- Performance optimization with materialized views
πExplore more
π GitHub Repository link: Online Learning Platform (SQL-Only Backend)
Conclusion
If you want to write clean, reusable, and powerful SQL queries, you must understand CTEs. They not only increase readability but also enable recursive logic and maintainable data transformations.
MATERIALIZED
control gives you more power when working with PostgreSQL, making it ideal for performance changes.
CTEs are more than simply a "nice-to-have"βthey are essential parts of complex SQL operations.
Share Your Thoughts
Have you ever used CTEs in your projects? What were the problems or benefits you experienced? Please let me know in the comments below!
Happy coding!β¨
Top comments (0)