Skip to main content

SF-0220 · Compare · Medium

What is the difference between UPDATE and UPSERT?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Both operations push changes to existing records, but they answer the question “what if the record doesn’t exist?” differently.

UpdateUpsert
Match keySalesforce Id (18-char)External Id field you nominate
Record missing?Fails the row with ENTITY_IS_DELETED / INVALID_CROSS_REFERENCE_KEYInserts a new record
Required prep on objectNoneA custom field marked External Id (usually Unique)
Created flag in resultn/atrue = inserted, false = updated
Typical callerOne-off admin edits, mass-cleanup jobsNightly integrations, syncs from external systems
AtomicityPure updateInsert-or-update per row

Why upsert exists

Imagine a nightly ERP feed. Half the accounts already exist in Salesforce, half are brand new from the last 24 hours. The integration doesn’t know which is which. With update you’d have to:

  1. Query Salesforce for known IDs.
  2. Split the feed into existing-vs-new.
  3. Run Insert for new, Update for existing.

That’s brittle and slow. Upsert collapses it into one operation by saying “match on this External Id; if found, update; if not, insert”. The integration stays stateless and idempotent — replay the same file twice and you get the same end state.

Why not always upsert?

Three good reasons to use plain Update instead:

  • You don’t have an External Id, and adding one to every object you load isn’t worth the schema cost.
  • You want loud failures. Updating by Id and failing on a missing record is sometimes the correct behaviour — it surfaces drift between systems.
  • Performance. Upsert has a tiny per-row overhead to look up the match. For bulk-edit work where you already have Salesforce Ids in the file, Update is the cleaner tool.

A worked example

You have 10,000 Accounts from your billing system. 7,000 already exist in Salesforce; 3,000 are brand new.

Update path:

  • 10,000 rows submitted.
  • 7,000 succeed.
  • 3,000 fail with INVALID_CROSS_REFERENCE_KEY (no matching Id).
  • You have to chase the failures separately.

Upsert path (matching on Billing_System_Id__c):

  • 10,000 rows submitted.
  • 7,000 update (Created = false).
  • 3,000 insert (Created = true).
  • Zero failures.

In a sentence

Update is modify-if-exists, fail-if-not. Upsert is modify-if-exists, create-if-not. The presence of an External Id field is what unlocks upsert.

Verified against: Data Loader Guide — Upserting Data, SOAP API Developer Guide — upsert(), Metadata API Developer Guide. Last reviewed 2026-05-17 for Spring ‘26 release.