What is a self join, and what kinds of question is it the only way to answer?
A self join is a join in which a table appears more than once, with a different alias for each appearance. The join condition relates the two copies to each other rather than to another table. It is the standard technique for three families of problem.
First, hierarchies. When a row points at another row in the same table through a parent key — employees and their managers, categories and their parents, accounts and their owners — a self join walks one level of the hierarchy, and a recursive CTE walks as many as you need. Second, duplicate detection: joining a table to itself on the business key and filtering for id inequality finds the pairs of rows that share a value. Third, comparison within a table: finding the previous or next row in a sequence, or finding every pair of rows where one is more expensive than another, is a self join on the same table with a self-referencing condition.
The one mechanical rule is that aliases are mandatory. When the same table appears twice, an unqualified column reference is ambiguous and the engine rejects the query. The alias is not a convenience; it is the only way to say which copy you mean. e.manager_id = m.id says "this copy's manager is that copy's id", which is the entire meaning of the query.
There is also a performance note. A self join is a join like any other, so the same rules apply about indexing the join key and about the row count multiplying: a parent with three children produces three rows per parent, so an aggregate over the result can be inflated unless you think about which copy you are counting.