Merging Records
Use the merge Statement
This example shows how to merge a duplicate account record into a main account record. The duplicate account has a related contact, which is moved to the main account record after the merge operation. After merging, the duplicate record is deleted and only the main record remains in the database.
1// Insert new accounts
2List<Account> ls = new List<Account>{
3 new Account(Name = 'Acme Inc.'),
4 new Account(Name = 'Acme')
5};
6insert as user ls;
7
8// Queries to get the inserted accounts
9Account mainAcct = [
10 SELECT Id, Name
11 FROM Account
12 WHERE Name = 'Acme Inc.'
13 WITH USER_MODE
14 LIMIT 1
15];
16
17Account dupAcct = [
18 SELECT Id, Name
19 FROM Account
20 WHERE Name = 'Acme'
21 WITH USER_MODE
22 LIMIT 1
23];
24
25// Add a contact to the account to be merged
26Contact c = new Contact(FirstName = 'Joe', LastName = 'Merged');
27c.AccountId = dupAcct.Id;
28insert as user c;
29
30try {
31 merge mainAcct dupAcct;
32} catch (DmlException e) {
33 // Process exception
34 System.debug('An unexpected error has occurred: ' + e.getMessage());
35}
36
37// After the account is merged with the main account,
38// the related contact is moved to the main record.
39mainAcct = [
40 SELECT Id, Name, (SELECT FirstName, LastName FROM Contacts)
41 FROM Account
42 WHERE Name = 'Acme Inc.'
43 WITH USER_MODE
44 LIMIT 1
45];
46
47Assert.isTrue(mainAcct.getSObjects('Contacts').size() > 0);
48Assert.areEqual('Joe', mainAcct.getSObjects('Contacts')[0].get('FirstName'));
49Assert.areEqual('Merged', mainAcct.getSObjects('Contacts')[0].get('LastName'));
50
51// Verify that the duplicate record is deleted
52Account[] result = [SELECT Id, Name FROM Account WHERE Id = :dupAcct.Id WITH USER_MODE];
53Assert.areEqual(0, result.size());Use the Database.merge Method
This second example is similar to the previous example, except that it uses the Database.merge method instead of the merge statement. The last argument of Database.merge is set to false, so any errors encountered in this operation are returned in the merge result without throwing exceptions. In the example, a main account and two duplicate account records are created. One of the duplicate account records has a child contact record. Through the merge operation, the contact is moved to the main account record, and the other records are deleted.
1// Create main account
2Account main = new Account(Name = 'Account1');
3insert as user main;
4
5// Create duplicate accounts
6List<Account> duplicates = new List<Account>{
7 // Duplicate account
8 new Account(Name = 'Account1, Inc.'),
9 // Second duplicate account
10 new Account(Name = 'Account 1')
11};
12insert as user duplicates;
13
14// Create child contact and associate it with first account
15Contact c = new Contact(FirstName = 'Joe', LastName = 'Smith', AccountId = duplicates[0].Id);
16insert as user c;
17
18// Get the account contact relation ID, which is created when a contact is created on "Account1, Inc."
19AccountContactRelation resultAcrel = [
20 SELECT Id
21 FROM AccountContactRelation
22 WHERE ContactId = :c.Id
23 WITH USER_MODE
24 LIMIT 1
25];
26
27// Merge duplicate accounts into main account
28Database.MergeResult[] results = Database.merge(main, duplicates, false);
29
30for (Database.MergeResult res : results) {
31 if (res.isSuccess()) {
32 // Get the main record ID from the result and validate it
33 System.debug('Main record ID: ' + res.getId());
34 Assert.areEqual(main.Id, res.getId());
35
36 // Get the IDs of the merged records and display them
37 List<Id> mergedIds = res.getMergedRecordIds();
38 System.debug('IDs of merged records: ' + mergedIds);
39
40 // Get the ID of the reparented record and validate that this is the contact ID.
41 System.debug('Reparented record ID: ' + res.getUpdatedRelatedIds());
42
43 // Make sure there are two IDs (contact ID and account contact relation ID); the order isn't defined
44 Assert.areEqual(2, res.getUpdatedRelatedIds().size());
45 Boolean flag1 = false;
46 Boolean flag2 = false;
47
48 // Because the order of the IDs isn't defined, the ID can be at index 0 or 1 of the array
49 if (resultAcrel.Id == res.getUpdatedRelatedIds()[0] || resultAcrel.Id == res.getUpdatedRelatedIds()[1]) {
50 flag1 = true;
51 }
52
53 if (c.Id == res.getUpdatedRelatedIds()[0] || c.Id == res.getUpdatedRelatedIds()[1]) {
54 flag2 = true;
55 }
56
57 Assert.isTrue(flag1);
58 Assert.isTrue(flag2);
59
60 } else {
61 for (Database.Error err : res.getErrors()) {
62 // Write each error to the debug output
63 System.debug(err.getMessage());
64 }
65 }
66}Merge Considerations
When merging sObject records, consider these rules and guidelines:
- Only leads, contacts, cases, and accounts can be merged. See sObjects That Don’t Support DML Operations.
- You can pass a main record and up to two additional sObject records to a single merge method.
- Field values on the main record, including null and empty field values, always supersede the corresponding field values on the records to be merged. Therefore, if a field value on the main record is empty, the resulting field value remains empty after the merge operation regardless of the field value on the duplicate record. To preserve a field value from a duplicate record, manually set this field value on the main record before performing the merge.
- External ID fields can’t be used with merge.