SELF JOIN
A self join joins a table to itself, typically using table aliases to compare rows within the same table to each other.
Overview
A self join is any join where a table is joined to another copy of itself. There's no special SQL syntax for it -- it's just a regular JOIN (INNER, LEFT, etc.) where the table on both sides happens to be the same one, distinguished by table aliases so column references aren't ambiguous.
Copy code
SELECT e.name AS employee, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.employee_id;
Here, employees is aliased as e (the employee's own row) and m (that employee's manager's row), letting the query compare each employee's manager_id to another row's employee_id within the same table.
Common use cases
- Hierarchical data: employee/manager relationships, category/parent-category trees, org charts (for deep, arbitrary-depth hierarchies, a recursive
WITH RECURSIVECTE is typically layered on top). - Comparing rows within the same entity: finding pairs of events, transactions, or records that relate to each other, e.g., finding consecutive orders by the same customer.
- Finding duplicates: joining a table to itself on a natural key to surface rows that share the same value but differ in some other column.
Copy code
-- find products with the same SKU but different prices
SELECT a.product_id, b.product_id, a.price, b.price
FROM products a
JOIN products b ON a.sku = b.sku AND a.product_id < b.product_id AND a.price <> b.price;
The a.product_id < b.product_id condition avoids matching each pair twice (once as (a,b) and once as (b,a)) and prevents a row from matching itself.
DuckDB notes
DuckDB has no special handling for self joins -- they use the same join execution (hash join, etc.) as joining two distinct tables, since aliasing makes the two references behave as independent relations during query planning.
Related terms
How do SQL JOINs work? Learn INNER, LEFT, RIGHT, FULL OUTER, CROSS, and ASOF joins with syntax examples — plus how to join directly against CSV and Parquet files.
NATURAL JOIN →A NATURAL JOIN automatically joins two tables on all columns that share the same name, without an explicit ON or USING clause.
CROSS JOIN →A CROSS JOIN produces the Cartesian product of two tables, pairing every row from the first table with every row from the second.
USING clause →The USING clause specifies join columns by name when both tables share identically named columns, joining on equality and returning a single copy of each shared column.
ASOF JOIN →An ASOF JOIN matches each row in one table to the closest preceding (or following) row in another table based on an ordering column, commonly a timestamp -- a core building block for time-series analysis.
Cost-based optimizer →A cost-based optimizer chooses among logically equivalent query plans by estimating the execution cost of each one, using statistics about table sizes and data distributions, and picking the cheapest estimate.
FAQS
No -- it's a regular JOIN where both sides reference the same table, distinguished with different table aliases so columns aren't ambiguous.
Modeling one level of a hierarchy, such as an employees table where each row references its manager's employee_id in the same table.
Add an inequality condition on a unique key, such as a.id < b.id, which both prevents a row from matching itself and avoids returning each pair twice in reversed order.
