Which one of the following triggers are optimized for preventing duplicate accounts?

trigger AccountDuplicateTrigger on Account (before insert,before update) {

    List<Account> existingAccount = new List<Account>([Select Id, Name From Account]); 

     for(Account a : Trigger.new){
         for(Account acc:existingAccount){
          a.adderror('You cannot create a dulplicate account');


trigger AccountDuplicateTrigger on Account (before insert,before update) {

     List<String> NameList=new List<String>();

    for(Account a:Trigger.new)

    List<Account> acc=[SELECT Name from Account where Name in NameList];

    for(Account a:trigger.new)
            a.addError('Duplicate account cannot be inserted');
  • 5
    The optimized solution is using the out-of-the-box Duplicate Management feature.
    – Adrian Larson
    Mar 1 '17 at 16:18
  • 2
    A wise man told me one day: "The less there is code, the less there is code to maintain". Mar 1 '17 at 16:20

As I mentioned in the comments, you shouldn't write code to implement this feature. Instead use out-of-the-box Duplicate Management.

If you really feel compelled to use Apex, you're on the right track with the second approach, but it won't work. You need to get a Set<String> existingNames.

Set<String> existingNames = new Set<String>();
for (Account record : [SELECT Name FROM Account WHERE Name IN :nameList])

for (Account record :  trigger.new)
    if (existingNames.contains(record.Name))
  • It's worth noting as @sfdcfox did below, that validations should be done in the after trigger. Because you cannot guarantee the order of in which triggers fire, two 'before' triggers could fire, and the second could change the name to be a duplicate after the one that performs the validation. Mar 1 '17 at 16:41
  • Another win for Duplicate Management as it fires after all before triggers.
    – Adrian Larson
    Mar 1 '17 at 16:44

The first trigger will result in a QueryLimit once more than 50,000 accounts exist in the database. Also, a loop over the entire database will eventually result in CPU limits being reached, probably even before you reach the 50,000 row query limit.

The second format also won't work, because NameList.size() will always be greater than 0, so no new records could ever be inserted once a single record exists in the database.

The more appropriate version of this code would need to check to see if the record queried matches a record in the trigger, because otherwise you'll query the record already in the trigger.

That looks like this:

trigger preventDup on Account(after insert, after update) {
    Map<String, Id> nameIds = new Map<String, Id>();
    for(Account record: Trigger.new) {
        if(nameIds.containsKey(record.Name)) {
            record.addError('Duplicate account not allowed.');
        } else {
            nameIds.put(record.Name, record.Id);
    for(Account record: [SELECT Name FROM Account WHERE Name IN :nameIds.keySet() FOR UPDATE]) {
        if(nameIds.get(record.Name) != record.Id) {
            Trigger.newMap.get(nameIds.get(record.Name)).addError('Duplicate account not allowed.');

Also note that I'm doing this in an 'after insert' to make sure we don't miss any records. I'm also using FOR UPDATE to make sure that we don't miss accounts inserted at (nearly) the same identical time.

  • Hi sfdcfox, When I tried this code, it is throwing an exception: Apex trigger preventDup caused an unexpected exception, contact your administrator: preventDup: execution of AfterInsert caused by: System.SObjectException: Invalid field SFDC Computing for Account: Trigger.preventDup: line 8, column 1. Also, it is preventing any new account to be inserted(which is not duplicate)
    – Shrishti
    Mar 1 '17 at 16:48
  • @Shrishti Sorry, that was a typo. I've fixed it.
    – sfdcfox
    Mar 1 '17 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.