Upsert Statements
Not Started

You get the best of both worlds
Chill it out, take it slow
Then you rock out the show
You get the best of both worlds
Mix it all together
And you know that it's the best of both worlds
The best of both worlds, yeah


Hannah Montana would have CLEARLY been a fan of upsert DML statements. An upsert allows you to either insert or update a sObject in just one call.

Internally, the operation looks to see if it can find a sObject record with the same Id. If it does, it will update the record. Otherwise, it'll insert the record. There are some exceptions listed here.

Let's use this operation to solve the issue we saw in the last lesson.

List<Account> accountsWithoutType = [SELECT Id, Name, Type FROM Account WHERE Type = '']; for (Account acc : accountsWithoutType) { acc.Description = 'Basic'; } Account newAcc = new Account(Name = 'Apple', Type='Basic'); accountsWithoutType.add(newAcc); update accountsWithoutType; // This threw an Exception



Changing update to upsert will allow the accounts from the SOQL query to be updated and the newAcc we created in memory to be created. Simple change update to upsert:

List<Account> accountsWithoutType = [SELECT Id, Name, Type FROM Account WHERE Type = '']; for (Account acc : accountsWithoutType) { acc.Description = 'Basic'; } Account newAcc = new Account(Name = 'Apple', Type='Basic'); accountsWithoutType.add(newAcc); upsert accountsWithoutType; // Hannah would be pleased



The linked article explains that the Id field of a sObject record is used by default to match the sObject with existing records. This matching process determines whether the sObject should be inserted as a new record or updated as an existing one.

You can specify a different field for matching, such as an ExternalId field or the Email field, on certain standard sObjects like Email or Contact.

To do this, you'll pass in the field token:

List<Contact> reconciliationContacts = new List<Contact>(); // Logic for retrieving & setting this list omitted. Imagine something like the above Account logic happens. upsert reconciliationContacts Contact.Fields.Email;



If you choose to do this, the field used as the matcher must be set on the upserted record. If not, you'll face another DML exception, like this: MISSING_ARGUMENT, Email not specified: [].

Challenge

You've given a method with two parameters: a list of Book__c records and a single Book record. When this method is called, sometimes the list of records already exists in the database; other times, it doesn't. The same is true for the single Book record.

Write a bulkified DML statement that can insert and update the records appropriately.