/** * ═══════════════════════════════════════════════════════════════════════════════ * BULK UPSERT WITH EXTERNAL ID * For data synchronization and migration scenarios * ═══════════════════════════════════════════════════════════════════════════════ * * PURPOSE: * Upsert (insert or update) records using an External ID field. * This is essential for: * • Data synchronization with external systems * • Data migration from legacy systems * • Ongoing integrations where records may or may not exist * * HOW IT WORKS: * • If External ID matches → UPDATE existing record * • If External ID not found → INSERT new record * • External ID field must be marked as "External ID" in Salesforce * * ═══════════════════════════════════════════════════════════════════════════════ */ // ═══════════════════════════════════════════════════════════════════════════════ // APEX UPSERT EXAMPLE // ═══════════════════════════════════════════════════════════════════════════════ /** * Example: Upsert Accounts using External_Id__c field * * Prerequisites: * 1. Create a custom field on Account: External_Id__c (Text, External ID, Unique) * 2. Ensure field is marked as "External ID" in field settings */ // Sample data simulating external system records List> externalData = new List>{ new Map{ 'externalId' => 'EXT-001', 'name' => 'External Account One', 'industry' => 'Technology', 'revenue' => 1000000 }, new Map{ 'externalId' => 'EXT-002', 'name' => 'External Account Two', 'industry' => 'Healthcare', 'revenue' => 2000000 }, new Map{ 'externalId' => 'EXT-003', 'name' => 'External Account Three', 'industry' => 'Finance', 'revenue' => 3000000 } }; // Build Account records with External ID List accountsToUpsert = new List(); for (Map data : externalData) { accountsToUpsert.add(new Account( External_Id__c = (String) data.get('externalId'), Name = (String) data.get('name'), Industry = (String) data.get('industry'), AnnualRevenue = (Decimal) data.get('revenue'), Type = 'Prospect', Description = 'Synced from external system' )); } // Upsert using External ID field Schema.SObjectField externalIdField = Account.External_Id__c; List results = Database.upsert(accountsToUpsert, externalIdField, false); // Process results Integer insertedCount = 0; Integer updatedCount = 0; Integer errorCount = 0; for (Integer i = 0; i < results.size(); i++) { Database.UpsertResult result = results[i]; Account acc = accountsToUpsert[i]; if (result.isSuccess()) { if (result.isCreated()) { insertedCount++; System.debug('✓ INSERTED: ' + acc.External_Id__c + ' -> ' + result.getId()); } else { updatedCount++; System.debug('✓ UPDATED: ' + acc.External_Id__c + ' -> ' + result.getId()); } } else { errorCount++; for (Database.Error err : result.getErrors()) { System.debug('✗ ERROR: ' + acc.External_Id__c + ' -> ' + err.getMessage()); } } } System.debug(''); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('UPSERT COMPLETE'); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('Inserted: ' + insertedCount); System.debug('Updated: ' + updatedCount); System.debug('Errors: ' + errorCount); System.debug('═══════════════════════════════════════════════════════════════'); // ═══════════════════════════════════════════════════════════════════════════════ // SF CLI BULK UPSERT // ═══════════════════════════════════════════════════════════════════════════════ /* STEP 1: Create CSV with External ID column Example accounts-upsert.csv: External_Id__c,Name,Industry,Type,AnnualRevenue,BillingCity,BillingState EXT-001,External Account One,Technology,Prospect,1000000,San Francisco,CA EXT-002,External Account Two,Healthcare,Customer,2000000,New York,NY EXT-003,External Account Three,Finance,Partner,3000000,Chicago,IL STEP 2: Run bulk upsert sf data upsert bulk \ --file accounts-upsert.csv \ --sobject Account \ --external-id External_Id__c \ --target-org myorg \ --wait 30 NOTES: • The External_Id__c field must exist and be marked as External ID in Salesforce • To use standard Id field as the external ID: --external-id Id • CSV must include the external ID column */ // ═══════════════════════════════════════════════════════════════════════════════ // EXAMPLE: UPSERT CONTACTS WITH ACCOUNT RELATIONSHIP // ═══════════════════════════════════════════════════════════════════════════════ /** * Upsert Contacts and establish Account relationship via External ID * * Prerequisites: * 1. Account.External_Id__c exists and has values * 2. Contact.External_Id__c exists */ /* List contactsToUpsert = new List(); // Create Contacts referencing Accounts by External ID for (Map data : externalContactData) { Contact con = new Contact( External_Id__c = (String) data.get('contactExternalId'), FirstName = (String) data.get('firstName'), LastName = (String) data.get('lastName'), Email = (String) data.get('email') ); // Set Account relationship via External ID // This creates the relationship without querying for the Account first con.Account = new Account( External_Id__c = (String) data.get('accountExternalId') ); contactsToUpsert.add(con); } // Upsert Contacts Database.upsert(contactsToUpsert, Contact.External_Id__c, false); */ // ═══════════════════════════════════════════════════════════════════════════════ // CSV FOR CONTACT UPSERT WITH ACCOUNT RELATIONSHIP // ═══════════════════════════════════════════════════════════════════════════════ /* Example contacts-upsert.csv: External_Id__c,FirstName,LastName,Email,Account.External_Id__c CON-001,John,Smith,john.smith@example.com,EXT-001 CON-002,Jane,Doe,jane.doe@example.com,EXT-001 CON-003,Bob,Johnson,bob.johnson@example.com,EXT-002 Note: Use "Account.External_Id__c" to reference parent by External ID sf data upsert bulk \ --file contacts-upsert.csv \ --sobject Contact \ --external-id External_Id__c \ --target-org myorg \ --wait 30 */ // ═══════════════════════════════════════════════════════════════════════════════ // EXAMPLE: UPSERT WITH STANDARD ID (Update existing records) // ═══════════════════════════════════════════════════════════════════════════════ /* When you have Salesforce IDs (from export), use Id as the external ID: Example updates.csv: Id,Name,Industry,AnnualRevenue 001XXXXXXXXXXXX1,Updated Account One,Technology,1500000 001XXXXXXXXXXXX2,Updated Account Two,Healthcare,2500000 sf data upsert bulk \ --file updates.csv \ --sobject Account \ --external-id Id \ --target-org myorg \ --wait 30 */ // ═══════════════════════════════════════════════════════════════════════════════ // COMPLETE SYNC WORKFLOW EXAMPLE // ═══════════════════════════════════════════════════════════════════════════════ /* FULL SYNC WORKFLOW: 1. Export current data from Salesforce sf data query \ --query "SELECT External_Id__c, Name, Industry FROM Account WHERE External_Id__c != null" \ --target-org myorg \ --result-format csv \ > current-accounts.csv 2. Generate sync file from external system (your process) - Include External_Id__c for each record - Include all fields to update/insert 3. Upsert the sync file sf data upsert bulk \ --file sync-accounts.csv \ --sobject Account \ --external-id External_Id__c \ --target-org myorg \ --wait 30 4. Check results sf data bulk results \ --job-id [job-id-from-step-3] \ --target-org myorg */ // ═══════════════════════════════════════════════════════════════════════════════ // BATCH APEX FOR COMPLEX UPSERT LOGIC // ═══════════════════════════════════════════════════════════════════════════════ /** * Batch class for upserting with custom logic */ public class BulkUpsertProcessor implements Database.Batchable, Database.Stateful { private String query; private Integer insertedCount = 0; private Integer updatedCount = 0; private Integer errorCount = 0; public BulkUpsertProcessor(String soqlQuery) { this.query = soqlQuery; } public Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator(query); } public void execute(Database.BatchableContext bc, List records) { // Transform and prepare records for upsert List accountsToUpsert = new List(); for (SObject record : records) { Account source = (Account) record; // Create upsert record Account target = new Account( External_Id__c = source.External_Id__c, Name = source.Name, Industry = source.Industry, AnnualRevenue = source.AnnualRevenue, LastModifiedDate = DateTime.now() // Will be auto-updated ); // Apply transformation logic if (target.AnnualRevenue != null && target.AnnualRevenue > 1000000) { target.Type = 'Enterprise'; } accountsToUpsert.add(target); } // Upsert with partial success List results = Database.upsert( accountsToUpsert, Account.External_Id__c, false // Allow partial success ); // Track results for (Database.UpsertResult result : results) { if (result.isSuccess()) { if (result.isCreated()) { insertedCount++; } else { updatedCount++; } } else { errorCount++; } } } public void finish(Database.BatchableContext bc) { System.debug('Batch Upsert Complete:'); System.debug(' Inserted: ' + insertedCount); System.debug(' Updated: ' + updatedCount); System.debug(' Errors: ' + errorCount); } } // Execute with: // String query = 'SELECT External_Id__c, Name, Industry, AnnualRevenue FROM Account WHERE External_Id__c != null'; // Database.executeBatch(new BulkUpsertProcessor(query), 200);