I have a table that looks like this:
category
IME, MySQL does not do well at optimizing sub-queries - particularly it doesn't seem to manage push-predicates.
I am a little bit conmfused about what the query is actually intended to return - particularly the 'sub-parent'
You'd get some improvement by putting left_id and right_id into a single index.
While you'll also get some improvement by unrolling the query into a stored procedure, given that you seem to be traversing almost the entire dataset each time a better solution would be to denormalise the tree depth and store it as an attribute for each node. Indeed you seem to be traversing it at least twice in the outer query alone.
However I notice that at the end of the query:
HAVING depth > 0
AND depth <= 1
Which surely is the same thing as
HAVING depth=1
Which then provides a very different way of optimizing the query (start by getting all the nodes where right=left+1 to find the nodes with no children and work up the way to check the category id).