First of all, it is very important to know that this question is very easy to answer if you know that you are working with a fixed set of generations (for example, to grandchildren). If this table ultimately will have many generations, and you want (for example) to find all of Kyle's descendants throughout the family tree, then you are not going to do this with a single query. (I have a stored procedure that deals with arbitrary levels of tree generations.) So for now, let's find grandparents / grandchildren.
As you said, finding grandparents is easy ...
mysql> select name from people where parent_id = 0; +-------+ | name | +-------+ | Kevin | | Kyle | +-------+ 2 rows in set (0.00 sec)
Now finding children is not so bad.
Let's find the children of Kyle:
mysql> select p1.name from people p1 where p1.parent_id in (select p2.id from people p2 where p2.name = 'Kyle'); +-------+ | name | +-------+ | John | | Jason | +-------+ 2 rows in set (0.02 sec)
And here are Kyle’s grandchildren:
mysql> select p3.name from people p3 where p3.parent_id in (select p2.id from people p2 where p2.parent_id in (select p3.id from people p3 where p3.name = 'Kyle')); +-------+ | name | +-------+ | Mabel | +-------+ 1 row in set (0.01 sec) mysql>
Going the other direction ... who is Mabel's parent?
mysql> select p1.name from people p1 where p1.id = (select p2.parent_id from people p2 where p2.name = 'Mabel'); +-------+ | name | +-------+ | Jason | +-------+ 1 row in set (0.00 sec) mysql>
... and her grandparents:
mysql> select p1.name from people p1 where p1.id = (select p2.parent_id from people p2 where p2.id = (select p3.parent_id from people p3 where p3.name = 'Mabel')); +------+ | name | +------+ | Kyle | +------+ 1 row in set (0.00 sec)
So you can see the pattern that I followed to make these requests if you need great-grandparents and great-grandchildren. However, the resulting query will become cumbersome if you need more generations, and the stored procedure in which the loops are executed will be fine.
The Oracle database has a more elegant solution, an SQL extension called "CONNECT BY PRIOR". For a more detailed reading (and an example of the MySQL stored procedure), check out the Connector for the previous MySQL equivalent here in StackOverflow.
One final note: do yourself a favor if you haven't already done so, and:
mysql> create index ix_parent_id on people(parent_id); Query OK, 0 rows affected (0.06 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql>
This will greatly improve performance for these queries.