63

I know of two approaches to get the recordtype id of a particular record type

  1. Use SOQL

    RecordType RecType = [Select Id From RecordType  Where SobjectType = 'Account' and DeveloperName = 'Business'];
    

Benifit here is if the admin changes the name of the record type, the code still works as we can use developer name

  1. Using Describe

    Map<String,Schema.RecordTypeInfo> rtMapByName = d.getRecordTypeInfosByName();
    Schema.RecordTypeInfo rtByName =  rtMapByName.get('Business');
    

Saves on SOQL limits but uses a describe call.

What would be your choice and why

EDIT : Usage is for setting up recordtype while inserting records in apex or trigger

2
  • 3
    I'm not sure there is a "best" answer, I think it will depend on the reason that you need it, and other constraints you may have.
    – Doug B
    Commented May 22, 2013 at 11:05
  • 1
    The new correct answer is to use getRecordTypeInfosByDeveloperName Commented Jun 13, 2018 at 18:43

10 Answers 10

75

If you want to develop an industrial-strength Force.com application, i.e. an AppExchange app, you CANNOT rely on either the Id or Name fields of RecordTypes as a means of getting at a desired RecordType for a given object. Why? Because the Id will be different in every customer's org, and the Name may be different in every customer's org AND for each Running User, depending on the user's language.

Therefore, it is ESSENTIAL to request RecordTypes by DeveloperName.

HOWEVER, it is also essential to note that there are TWO pieces of information about a RecordType that you need to know about: is it Active in the org (meaning, anyone in the org could potentially use it), and is it Available to the running user (a Profile/Permission Set setting). The IsActive field is available on the RecordType object, as it is global, and user/profile agnostic. The isAvailable property is available through the RecordTypeInfo object, and therefore should be acquired through the Schema methods.

Here is a utility method, VERY suitable for caching, that, for a given Schema.SObjectType, returns a Map of that Object's Active AND Available RecordType Ids, keyed by DeveloperName:

//Record types cache
private static Map<Schema.SObjectType,Map<String,Id>> rtypesCache;
private static List<sObject> results;
static {
    rtypesCache = new Map<Schema.SObjectType,Map<String,Id>>();//convenient map, formatted from results.
    results = new List<sObject>();//contains all recordtypes retrieved via SOQL
}

// Returns a map of active, user-available RecordType IDs for a given SObjectType,
// keyed by each RecordType's unique, unchanging DeveloperName 
private static Map<String, Id> getRecordTypeMapForObjectGeneric(Schema.SObjectType token) {
    // Do we already have a result? 
    Map<String, Id> mapRecordTypes = rtypesCache.get(token);
    // If not, build a map of RecordTypeIds keyed by DeveloperName
    if (mapRecordTypes == null) {
        mapRecordTypes = new Map<String, Id>();
        rtypesCache.put(token,mapRecordTypes);
    } else {
       // If we do, return our cached result immediately!
       return mapRecordTypes;
    }

    // Get the Describe Result
    Schema.DescribeSObjectResult obj = token.getDescribe();


    //Check if we already queried all recordtypes.
    if (results == null || results.isEmpty()) {
    // Obtain ALL Active Record Types
    // (We will filter out the Record Types that are unavailable
    // to the Running User using Schema information)
        String soql = 'SELECT Id, Name, DeveloperName, sObjectType FROM RecordType WHERE IsActive = TRUE';
        try {
            results = Database.query(soql);
        } catch (Exception ex) {
            results = new List<SObject>();
        }
    }

    // Obtain the RecordTypeInfos for this SObjectType token
    Map<Id,Schema.RecordTypeInfo> recordTypeInfos = obj.getRecordTypeInfosByID();
    // Loop through all of the Record Types we found,
    // and weed out those that are unavailable to the Running User
    for (SObject rt : results) { 
        if (recordTypeInfos.get(rt.Id) != null) {
            if (recordTypeInfos.get(rt.Id).isAvailable()) {
                // This RecordType IS available to the running user,
                //      so add it to our map of RecordTypeIds by DeveloperName
                mapRecordTypes.put(String.valueOf(rt.get('DeveloperName')),rt.Id);
            }
            else {
                System.debug('The record type ' + rt.get('DeveloperName') + ' for object ' + rt.get('sObjectType') + ' is not availiable for the user.');
            }
        }
    }

    return mapRecordTypes;
}

TO use this to accomplish your need within a Trigger or other Apex scenario, you can just do this:

// Get all Active Account RecordTypes that are available to the running user
Map<String,Id> accountTypes = Utils.GetRecordTypeIdsByDeveloperName(Account.SObjectType);
for (Account a : Trigger.new) {
    // ATTEMPT to assign a different RecordType to this Account
    // if it has tons of employees.
    // We can ONLY attempt, because the RecordType may have been deactivated,
    // and the RecordType may not be available to the Running User,
    // depending on Profile settings.
    if (a.NumberOfEmployees > 10000 
    && accountTypes.containsKey('Really_Stinking_Big_Account')) {
         a.RecordTypeId = accountTypes.get('Really_Stinking_Big_Account');
    } else if (accountTypes.containsKey('Small_Peanuts_Account')) {
         a.RecordTypeId = accountTypes.get('Small_Peanuts_Account');
    }
}

Notice that we do NOT assume that a given RecordType is either Active or Available to the running user --- we dynamically check. This is essential to consider when leveraging record types in Managed Package code.

Also notice that because we look for RecordTypes by DeveloperName, this code is completely organization and user agnostic. It will work no matter which org we are in, no matter how the org's admins have overridden or customized your expected use of your installed record types, and no matter which Translations of your RecordTypes' Names are being used.

Finally, notice that because we do a Dynamic SOQL query, this code will allow your AppExchange app to be used in Professional Edition or Group Edition, which do not have RecordTypes. Any hard-coded references to non-supported objects will cause your app to be disqualified from org editions which do not support such objects --- but use of Apex system classes and methods is fine no matter what features your org supports, therefore getRecordTypeInfos() can be safely used.

6
  • Shouldn't the GetRecordTypeIdsByDeveloperName method return immediatelly in case of cache hit? Otherwise what is the purpose of using static map to cache record types?
    – IvanR
    Commented Sep 9, 2014 at 11:12
  • @Ivan Hah! Wow. That is exactly what it should be doing. I've edited to correct. Good catch!
    – zachelrath
    Commented Sep 9, 2014 at 15:20
  • also shouldn't the rtypescache = new Map<Schema.SObjectType,Map<String,Id>>(); ? Commented Apr 1, 2015 at 1:36
  • @Xtremefaith good catch, I corrected this.
    – zachelrath
    Commented Apr 6, 2015 at 14:05
  • 6
    This is great, but no longer needed. In Summer '18 (v43.0) Salesforce added the much-needed getRecordTypeInfosByDeveloperName. See my solution to this question for an example. Commented Jun 13, 2018 at 18:51
33

Until now, we used zachelrath's solution, and it worked wonderfully.

As of v43.0 (Summer '18), however, the proper way to do this is to simply use getRecordTypeInfosByDeveloperName. It eliminates a good chunk of code, eliminates a database hit (SOQL limit), and preserves the stability of using the RecordType's DeveloperName. Also, there are no longer limits on describe calls, so that constraint is gone.

For example:

SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Business');
3
  • 1
    thanks for posting this Charles, I'd been meaning to come post this as well as soon as Summer 18 was released. Definitely a lot nicer to have a native method instead, at last!
    – zachelrath
    Commented Jun 14, 2018 at 13:12
  • @zachelrath Sure. I had an attribution in my RecordTypeUtility class pointing to your solution above. Commented Jun 14, 2018 at 18:40
  • In addition to the benefits @CharlesKoppelman mentioned, I ran some tests and estimated that this approach runs about 6x faster than the SOQL approach. Commented Jun 8, 2020 at 18:22
19

Schema.SObjectType.Object__c.RecordTypeInfosByName.get('MyType').RecordTypeId

The cached Schema.SObjectType is often my most preferable approach because:

  • no SOQL queries,
  • no describe calls,
  • no function invocations,

BUT watch out if your customer will mess with the labels:

override labels [...] apply to the Schema.RecordTypeInfo objects

They are fetched by Name, not DeveloperName, and there's no way to make a static reference!

5
  • 2
    I like this herp Commented May 22, 2013 at 14:05
  • 1
    Tempting but I've run it in Exec. Anonymous and got same result as the one shown by Sameer Miraj. It does use up a Describe...
    – eyescream
    Commented May 22, 2013 at 15:56
  • 1
    @eyescream the blog post links to a class of mine that does some intelligent caching and has some neat helpers: github.com/capeterson/Apex-Util/blob/master/src/classes/… Commented May 22, 2013 at 17:17
  • 4
    It kills me that SFDC architected record type describes this way. Everywhere else the api/developer name is the key, except for record type describes. No idea what happens what the describe returns when two record types have the same label ... Commented May 28, 2013 at 20:03
  • I don't see this documented anywhere, but this code works: Schema.SObjectType.Object__c.RecordTypeInfosByDeveloperName.get('MyType').RecordTypeId. Performance-wise, it seems to be roughly identical to getRecordTypeInfosByDeveloperName(). Commented Jun 8, 2020 at 18:16
7

If the "need" (trigger, VF page controller) is relatively simple - I tend to use describes. They're underestimated a bit I think and it's far less likely to hit describes limit vs queries limit.

If the scenario is complex, multiple objects inserted at the same time - I'd go for SOQL. Because in worst case scenario you waste only 1 query to fetch them all and store in Map<String, Map<String, Id>> (that's SObjectType => DeveloperName => Id). Put it into a static variable of an utility class and you have easy access to all rec. types in scope of current transaction.

Last but not least - you could always filter out the unused record types on user's profiles. Or instruct them that they can select their defaults in setup. Not really a reliable solution but it's there.

2
  • This is exactly what we do. I wrote a utility class because our core CRM objects were getting cluttered with triggers from different business units filtering on RecordType first. Then the first person to retrieve their RecordType from a trigger kicks off the loading of the static map. Any subsequent call looks to the existing map for the info.
    – drakored
    Commented Jun 6, 2013 at 9:28
  • 2
    Describes are awesome!! with one gigantic exception, record types are keyed by label not api name, wtf salesforce!! Commented Jun 7, 2013 at 16:45
5

I think using the describe is better. In a recent assignment of mine we had a scenario where we were hitting the SOQL limit when we do the Run all test. So we had to replace all queries with describe.

Using the below statements counts as against the governer limit:

Schema.SObjectType.Account.RecordTypeInfosByName.get('Partner').RecordTypeId

enter image description here

2
  • 3
    At a place where I used to work, they used Describes extensively and ran into problems hitting the describe limit. So I think you need to consider the org you are working in. In your case where SOQL limits are the major problem, Describe is a neat solution. In other cases it may be different. It's a good point, to consider the limits on both calls.
    – Doug B
    Commented May 22, 2013 at 11:17
  • I agree with Doug..
    – Sam
    Commented May 22, 2013 at 11:54
3

First off, if you're hitting SOQL limits because of a single record type query, then you have many other issues that you should be dealing with first.

Secondly, for record types and other ids that I'll be re-using through out Apex, I will throw them in a custom setting and cache them, using logic similar to:

List<Account> accts = new List<Account>();
Id recTypeId = MyRecordTypeSetting__c.getValues('MyRecordTypeDeveloperName');
if(recTypeId != null){
    accts = [Select Id From Account Where RecordTypeId = :recTypeId];
}
3
  • 1
    Nice one, I've used to store rec. types that way... But you need to populate the setting on every refresh of Developer/Config sandbox.
    – eyescream
    Commented May 22, 2013 at 17:25
  • 1
    Not after Summer '13 :) Commented Jun 10, 2013 at 20:20
  • The sooner it comes the better :) Although my favorite would be approval process metadata (click-click-aaargh-somebody-kill-me) and more fine-grained control over report folders ("what do you mean I can't edit this report, I've created it 5 mins ago! Does that mean I'd be better off not sharing my awesome dashboard with the rest of the team?"). Eh.
    – eyescream
    Commented Jun 10, 2013 at 20:58
3

Please make sure your org is updated to Version 43.

Id devRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('PersonAccount').getRecordTypeId();
System.debug('Record type id is => '+devRecordTypeId);
2

FYI Describe limits have been removed as of Summer 2015! Yay! But still a little boo that we have to use the label name :(

1

Usually we access record type with the help of SOQL which count in Salesforce SOQL governors limit.

Instead of SOQL you can try below line:

Id customerAccountRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('CustomerAccount').getRecordTypeId();

You can check : The Ultimate Guide To Get Sobject Record Type Id By Record Type Name Without Soql

0

The question is, why do you want the record type id? Is it so that you can limit your query? In that case

Select Id
From account
where RecordType.developername = 'Business'

and you have saved a query :)

You can also use it on related objects

Select Id
From case
where account.RecordType.developername = 'Business'

And if you really need the ID for some reason, maybe you can get it while you are getting your data?

Select Id, RecordTypeID
From account
1
  • Its for setting up recordtypes while inserting data through triggers or apex
    – Prady
    Commented May 22, 2013 at 11:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .