Assuming there is exactly one level of hierarchy possible like your example suggests:
SELECT child.*
FROM tbl AS child
LEFT JOIN tbl AS parent ON parent.id = child.parent_id
ORDER BY COALESCE(parent.id, child.id) -- order by parent if exists
, parent.id IS NOT NULL -- parent first per group
, child.id; -- order rest by id
The join is only needed if we sort by some additional attribute like a "name" (which is the typical case as values of surrogate IDs have no meaning). While only sorting by ID like you demonstrate, we don't need the join as all information is already there (like also demonstrated by Gordon). Then we can simplify:
SELECT *
FROM tbl
ORDER BY CASE WHEN parent_id = 0 THEN id ELSE parent_id END
, parent_id <> 0
, id;
- The 2nd
ORDER BY
item is needed to sort parent before its children. Works because FALSE
sorts before TRUE
. See:
- The last
ORDER BY
item is only required if there can be multiple children.
db<>fiddle here - with extended test case to demonstrate relevance of ORDER BY
items.