Getting the highest Parent Account in Salesforce Hierarchy via SOQL/APEX

I am trying to get the highest level of parent account in an Account Hierarchy in Salesforce via Apex and SOQL.
I want to create and MAP<Id, Set<Account>> hierarchy in my Apex class to where the highest parent account’s Id is the Id in the Map while all the child accounts (or at any level) are in the Set.

I have written the following recursive function to do this but I can’t get all the child Accounts in there for the top most parent account.

public class AccountsHierarchy{

    public Map<Id, Account> allChildAccounts {get; private set;}
    private Map<String, Set<Account>> parentAccountMap {get; set;}
    private List<Account> childAccounts {get; set;}

    public AccountsHierarchy(){

        allChildAccounts = new Map<Id, Account>([
                SELECT Id, ParentId, Parent.ParentId
                FROM Account
                WHERE ParentId != NULL
        childAccounts = new List<Account>();
        parentAccountMap = getParentAccountMap();


    private String getParentAccountId(String childAccountId){

        Account localAccount = allChildAccounts.get(childAccountId);
        if(localAccount.Parent.ParentId != null) {

            return getParentAccountId(localAccount.ParentId);

            return localAccount.ParentId;


    public Map<String, Set<Account>> getParentAccountMap(){

        Map<String, Set<Account>> parentAccounts = new Map<String, Set<Account>>();
        for(String childAccountId:allChildAccounts.keySet()){


            String parentAccountId = getParentAccountId(childAccountId);

            if(parentAccountId != null){

                    Set<Account> childAccountsFromMap = parentAccounts.get(parentAccountId);
                    parentAccounts.put(parentAccountId, childAccountsFromMap);
                } else {
                    parentAccounts.put(parentAccountId, new Set<Account>(childAccounts));

        return parentAccounts;



Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The best approach to this would be, in my opinion, to create a hidden field to store the Id of the account on the top of the hierarchy, and fill it with a trigger, process or workflow rule.

Scenario 1:

  1. Top account is created
  2. Child account is created, but parent account is not related yet.
  3. User remembers that the accounts are related, and fills the “parent account” field.
  4. Have the system update the “account on top of the hierarchy” field with either the value of the same field on the parent record (if it isn’t blank) or fill it with the parent record’s Id.

Scenario 2:

  1. Top account and child account are created.
  2. User creates a third account that will now act as the parent for the other two.
  3. User fills the “parent account” on the old parent account record with the new top account created.
  4. Have the system update the “account on top of the hierarchy” field on the child records, if the account is the new parent or not (checking the same field, again).

Scenario 3:

  1. Top account, middle account and bottom account are created.
  2. User creates another account that will sit between two of those.
  3. User fills the “parent account” of the new account with the referente of any of the accounts currently in the database.
  4. If the account selected as parent is the top one, check if the “account on top of the hierarchy” field of the parent record is empty. If it is not, then the created account should copy the parent’s account field. If it is empty, then the parent account is the top account, and the system should copy the account Id instead.

These should cover the most common scenarios, I believe. That way you will not have to worry about hitting governor limits when searching for the account on top.

It might be a good thing to do a little research if there is such situation where multiple accounts are linked through the ParentId field. If there aren’t that many levels, then you’ll probably be fine using the query inside the loop as an exception, because it is indeed simpler than doing all this. But if you don’t know how many accounts can be nested or the business requires that the accounts are nested, then it might be a good idea to invest some time in this kind of solution.

Method 2

I am working on a similar issue I found alot of help and a solution to this using the following website

    //find highest parent account from a child
public id findTopLevelParent(account a) {
    Boolean isTopLevelAccount = false;
    account acct = new account();
    id currAcctId =;

    while (!isTopLevelAccount) {

        acct = [select Id, ParentId From Account where Id = :currAcctId limit 1];
        if (acct.ParentID != null) {
            currAcctId = acct.ParentID;
        } else {
            isTopLevelAccount = true;

    return currAcctId;   

From that website I Got this piece of code working.

As for the Map part, heres the link to the flushed out method from that link I shared earlier.

I do not know if this is the best solution, So definetly open to critique

Method 3

Here is the most aggressive SOQL query available to find the top level Account from any starting Account Id:

select ParentId, Parent.ParentId, Parent.Parent.ParentId,
Parent.Parent.Parent.ParentId, Parent.Parent.Parent.Parent.ParentId,
Parent.Parent.Parent.Parent.Parent.ParentId from Account where

Here is an example with the maximum of 6 Parent Ids returned. If you still have not reached the top ParentId=NULL then you must recurse on the last returned Parent Id and run the query again until you get there.

    <result xsi:type="QueryResult">
        <queryLocator xsi:nil="true"/>
        <records xsi:type="sf:sObject">
            <sf:Id xsi:nil="true"/>
            <sf:Parent xsi:type="sf:sObject">
                <sf:Id xsi:nil="true"/>
                <sf:Parent xsi:type="sf:sObject">
                    <sf:Id xsi:nil="true"/>
                    <sf:Parent xsi:type="sf:sObject">
                        <sf:Id xsi:nil="true"/>
                        <sf:Parent xsi:type="sf:sObject">
                            <sf:Id xsi:nil="true"/>
                            <sf:Parent xsi:type="sf:sObject">
                                <sf:Id xsi:nil="true"/>

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Notify of

Inline Feedbacks
View all comments
Would love your thoughts, please comment.x