Salesforce Apex DML statements are used to create, modify, delete, restore, and merge records from Apex code. DML means Data Manipulation Language. In Apex, you can run DML either with direct DML keywords such as insert and update, or with methods from the Database class when you need more control over error handling.

This tutorial explains each Salesforce Apex DML operation with syntax, examples, and practical notes about partial success, bulk DML, triggers, and common mistakes.

Salesforce Apex DML Statements

Salesforce Apex DML (Data Manipulation Language) statements are used to Insert, Update, Merge, Delete and restore data in Salesforce. We can perform DML operations using the Apex DML statements or the methods of the Database class. We can perform 6 DML operations they are

  1. Insert.
  2. Update.
  3. Delete.
  4. UpSert.
  5. Merge and
  6. Restore.

In daily Apex development, these operations are used with standard objects such as Account, Contact, Lead, and Opportunity, as well as custom objects. A DML operation can run on a single sObject record or on a list of sObjects. In production code, using lists is usually preferred because it helps keep Apex bulk-safe.

Salesforce Apex DML statements

Salesforce Apex DML operations at a glance

DML operationWhat it does in ApexTypical use case
insertCreates new records.Add new Accounts, Contacts, Leads, or custom object records.
updateModifies existing records.Change field values on records that already have an Id.
upsertCreates new records or updates existing records.Load or sync records using record Id or an external Id field.
deleteMoves records to the Recycle Bin.Remove records that should no longer be active.
undeleteRestores records from the Recycle Bin.Recover deleted records when they are still restorable.
mergeCombines duplicate records of the same object type.Merge duplicate Accounts, Contacts, or Leads.

For official syntax and behavior details, refer to the Salesforce Apex Developer Guide on Apex DML operations, the Apex DML reference, and Salesforce Trailhead’s module on manipulating records with DML.

Insert DML Statement

The insert DML operation adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. insert is analogous to the INSERT statement in SQL.

Use insert when the record does not already exist in Salesforce. After a successful insert, Salesforce assigns an Id to the record. Required fields, validation rules, duplicate rules, triggers, flows, and other automation can affect whether the insert succeeds.

Syntax

</>
Copy
insert sObject

insert sObject[]

Example

</>
Copy
Account newAcct = new Account(name = 'Acme');
try {
   insert newAcct;
} catch (DmlException e) {
// Process exception here
}

In real projects, prefer inserting a list of records outside loops instead of inserting one record at a time. This helps reduce the number of DML statements used in a transaction.

</>
Copy
List<Account> accountsToInsert = new List<Account>();
accountsToInsert.add(new Account(Name = 'Account A'));
accountsToInsert.add(new Account(Name = 'Account B'));

insert accountsToInsert;

Update DML Statement

The update DML operation modifies one or more existing sObject records, such as individual accounts or contacts, invoice statements, in your organization’s data. Update is analogous to the UPDATE statement in SQL.

Use update only when the record already has a valid Salesforce record Id. You do not need to query every field before updating. In many cases, you can create an sObject instance with the record Id and the fields you want to change.

 Syntax

</>
Copy
update sObject

update sObject[]

Example

</>
Copy
Account a = new Account(Name='Acme2');
insert(a);

Account myAcct = [SELECT Id, Name, BillingCity FROM Account WHERE Id = :a.Id];
myAcct.BillingCity = 'San Francisco'; 

try {
    update myAcct;
} catch (DmlException e) {
    // Process exception here
}

The same update can also be written by setting only the record Id and the changed field. This is useful when you already know the Id and do not need to read the old field values.

</>
Copy
Account accountToUpdate = new Account(
    Id = a.Id,
    BillingCity = 'San Francisco'
);

update accountToUpdate;

Upsert DML Statement

The upsert DML operation creates new records and updates sObject records within a single statement, using a specified field to determine the presence of existing objects, or the ID field if no field is specified.

Use upsert when your Apex code may receive a mix of new and existing records. If you specify an external Id field, Salesforce uses that field to decide whether to insert or update. If you do not specify a field, Salesforce uses the record Id.

 Syntax

</>
Copy
upsert sObject?? [opt_field]
upsert sObject[]?? [opt_field]

Example

</>
Copy
List<Account> acctList = new List<Account>();
// Fill the accounts list with some accounts

try {
    upsert acctList;
} catch (DmlException e) {
   
}

A common upsert pattern is integration code that receives an external account key from another system. The Salesforce field used for matching must be configured as an external Id field when you want to upsert by that field.

</>
Copy
List<Account> accountsToUpsert = new List<Account>{
    new Account(Name = 'Acme India', External_Account_Id__c = 'EXT-1001'),
    new Account(Name = 'Acme USA', External_Account_Id__c = 'EXT-1002')
};

upsert accountsToUpsert External_Account_Id__c;

Delete DML Statement

The delete DML operation deletes one or more existing sObject records, such as individual accounts or contacts, from your organization’s data. Delete is analogous to the delete() statement in the SOAP API.

In Salesforce, deleting a record normally moves it to the Recycle Bin rather than immediately removing it permanently. Deleting records can also affect related records depending on relationship settings, sharing, automation, and validation logic.

Syntax

</>
Copy
delete sObject
delete sObject[]

Example

</>
Copy
Account[] doomedAccts = [SELECT Id, Name FROM Account 
                         WHERE Name = 'DotCom']; 
try {
    delete doomedAccts;
} catch (DmlException e) {
    // Process exception here
}

Before deleting records in Apex, query only the records that should be deleted and make the filter condition specific. A broad delete query can remove more records than intended.

Undelete DML Statement

The undelete DML operation restores one or more existing sObject records, such as individual accounts or contacts, from your organization’s Recycle Bin. undelete is analogous to the UNDELETE statement in SQL.    

Use undelete when deleted records are still available for restore. To query deleted records from Apex, use ALL ROWS in the SOQL query. Restored records may again run automation depending on your org configuration and record behavior.

Syntax

</>
Copy
undelete sObject | ID
undelete sObject[] | ID[]

Example

</>
Copy
Account[] savedAccts = [SELECT Id, Name FROM Account WHERE Name = 'Universal Containers' ALL ROWS]; 
try {
    undelete savedAccts;
} catch (DmlException e) {
    // Process exception here
}

Merge DML Statement

The merge statement merges up to three records of the same sObject type into one of the records, deleting the others, and re-parenting any related records.This DML operation does not have a matching Database system method.

Use merge when duplicate records need to be combined. The first record is the master record that remains. The other record or records are merged into the master and then deleted. Merge is commonly used with Accounts, Contacts, and Leads.

Syntax

</>
Copy
merge sObject sObject

merge sObject sObject[]

merge sObject ID

merge sObject ID[]

Example

</>
Copy
List<Account> ls = new List<Account>{new Account(name='Acme Inc.'),new Account(name='Acme')};
insert ls;
Account masterAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme Inc.' LIMIT 1];
Account mergeAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1];
try {
    merge masterAcct mergeAcct;
} catch (DmlException e) {
    // Process exception here
}

When using merge, check the master record carefully. Field values, ownership, related records, and automation behavior can affect the final data after the merge.

Different ways to perform DML operations.

  1. By using DML Statements
  2. By using Database Class.

By using DML Statements.

</>
Copy
List <Account> accList = new List<Account>();
accList.add (new Account(Name='Account1'));
accList.add(new Account(Name='Account2'));
insert accList;

By using Database Class.

</>
Copy
List <Account> accList = new List<Account>();
accList.add (new Account(Name='Account1'));
accList.add(new Account(Name='Account2'));
Database.SaveResult[] Sr = Database.insert(acctList,false);

As shown above, there is one difference between the two operations. In the database class method, we can specify whether to allow partial processing of the records if any errors are encountered by paring the boolean values as a parameters to Database.insert.

  • If we give the parameter as ‘True’. if any error occurs it doesn’t allow the operation to continue.
  • If we give the parameter as ‘false’, the remaining DML operations can still succeed. Where as insert in DML if any one of the record fails the total operation is discarded.

In other words, direct DML statements behave like an all-or-none operation. If one record in the list fails, the statement throws a DmlException and the transaction is rolled back unless the exception is handled in a larger transaction design. With Database.insert(records, false), valid records can succeed while invalid records return errors in the result array.

Apex DML statements versus Database class methods

FeatureDML statementDatabase class method
Exampleinsert accounts;Database.insert(accounts, false);
Error handlingThrows DmlException.Can return result objects such as Database.SaveResult.
Partial successNot available directly.Available by passing false for allOrNone.
Best useSimple operations where all records should succeed or fail together.Data loads, integrations, and batch logic where record-level errors must be captured.

Handling Apex DML errors with SaveResult

When using the Database class with allOrNone set to false, Apex returns one result for each record. This lets you identify which records succeeded and which records failed without losing all valid records.

</>
Copy
List<Account> accountsToInsert = new List<Account>{
    new Account(Name = 'Valid Account'),
    new Account()
};

Database.SaveResult[] results = Database.insert(accountsToInsert, false);

for (Integer i = 0; i < results.size(); i++) {
    if (results[i].isSuccess()) {
        System.debug('Inserted record Id: ' + results[i].getId());
    } else {
        for (Database.Error err : results[i].getErrors()) {
            System.debug('Record index ' + i + ' failed: ' + err.getMessage());
        }
    }
}

This approach is useful in batch Apex, queueable jobs, scheduled jobs, and integrations where you need to log failed records and continue processing the remaining records.

Bulk-safe Salesforce Apex DML best practices

Apex runs on a multi-tenant platform, so DML usage is governed by transaction limits. Good Apex code collects records in lists and performs DML once per logical operation instead of repeatedly inside loops.

Avoid Salesforce DML inside for loops

The following pattern should be avoided because it performs one DML statement for each record in the loop.

</>
Copy
// Avoid this pattern.
for (Account acct : accounts) {
    acct.Description = 'Updated by Apex';
    update acct;
}

Instead, prepare all records first and run a single DML statement on the list.

</>
Copy
List<Account> accountsToUpdate = new List<Account>();

for (Account acct : accounts) {
    acct.Description = 'Updated by Apex';
    accountsToUpdate.add(acct);
}

if (!accountsToUpdate.isEmpty()) {
    update accountsToUpdate;
}

Check CRUD and field access before Apex DML when required

Apex generally runs in system context, so developers should consider object and field permissions where the code is expected to respect user access. Use appropriate security checks, sharing design, and field-level access handling based on the entry point and business requirement.

Understand automation triggered by Salesforce DML

DML can fire triggers, flows, validation rules, assignment rules, duplicate rules, roll-up updates, and other automation. When debugging DML failures, read the full error message because the failure may come from automation rather than from the DML statement itself.

Common Salesforce Apex DML mistakes

  • Using DML inside loops: collect records in a list and run one DML operation outside the loop.
  • Updating records without Id values: update, delete, and undelete need records or Ids that already exist.
  • Using upsert without a clear matching field: specify the external Id field when the match should not be based on Salesforce record Id.
  • Ignoring partial success results: when using Database.insert(records, false), always inspect the returned result array.
  • Not planning for automation errors: validation rules, flows, and triggers can make an otherwise valid DML statement fail.

Salesforce Apex DML FAQ

What is DML in Salesforce Apex?

DML in Salesforce Apex means Data Manipulation Language. It is used to insert, update, upsert, delete, undelete, and merge Salesforce records from Apex code.

What is the difference between insert and upsert in Apex?

insert creates only new records. upsert either creates a new record or updates an existing record depending on the record Id or the specified external Id field.

When should I use the Database class instead of direct DML statements?

Use the Database class when you need partial success, detailed record-level errors, or result objects such as Database.SaveResult. Use direct DML statements when the operation is simple and all records should succeed or fail together.

Can Salesforce Apex DML run on a list of records?

Yes. Most Apex DML operations can run on a single sObject or a list of sObjects. Running DML on lists is the recommended pattern for bulk-safe Apex code.

Why does an Apex DML statement fail even when the syntax is correct?

A DML statement can fail because of missing required fields, validation rules, duplicate rules, lookup constraints, record locks, triggers, flows, permission-related design issues, or other automation in the Salesforce org.

Editorial QA checklist for this Salesforce Apex DML tutorial

  • Confirm that every DML operation listed here is an Apex record operation: insert, update, upsert, delete, undelete, and merge.
  • Check that examples using lists do not place DML statements inside loops.
  • Verify that Database.insert(records, false) examples inspect result objects instead of assuming every record succeeded.
  • Review upsert examples to ensure the external Id field name is clearly identified as an example field.
  • Confirm that the tutorial explains automation-related DML failures, not only syntax errors.