Im working on inserting multiple campaign records with a self-relationship. I was hoping to do this with one DML statement using the method described here: Link I know in the first paragraph it says two different SObject types but was hoping there is a work around to this. Here is my current code:

@Method Name    : cloneParentChildCampaigns
@description    : This method clones parent and related child campaigns for end of year.
@param          : None
@return         : None
@TestVisible private void cloneParentChildCampaigns(){
    List<Campaign> currentPCList = new List<Campaign>();
    List<SObject> clonedPCList = new List<SObject>();
    List<Campaign> currentCCList = new List<Campaign>();
    List<SObject> clonedCCList = new List<SObject>();
    SObject[] insertSObjectList = new List<SObject>();
    Map<Id, Campaign> pcReferenceMap = new Map<Id, Campaign>();
    Integer sequentialNum = 0;

    // Create and run query for parent campaigns of the current year
    date begOfYear = date.parse('1'+'/'+'1'+'/'+ today.year());
    date endOfYear = date.parse('12'+'/'+'31'+'/'+ today.year());
    String pcQuery = 'SELECT Id, Name, isActive, startDate, endDate, actualCost, status FROM Campaign WHERE ParentId = null AND isActive = true AND startDate = :begOfYear AND endDate = :endOfYear ORDER BY ID ASC';
    currentPCList = Database.query(pcQuery);
    system.debug('currentPCList : ' + currentPCList);
    String ccQuery = 'SELECT Id, ParentId, Name, isActive, startDate, endDate, actualCost, status FROM Campaign WHERE ParentId != null AND isActive = true AND startDate = :currentStartDate AND endDate = :currentEndDate ORDER BY ID ASC';
    currentCCList = Database.query(ccQuery);
    system.debug('currentCCList : ' + currentCCList);

    // Loop through all child campaigns
    for (Campaign currentCC : currentCCList) {
        Campaign clonedCC = currentCC.clone(false,true,true,true);
            // Assign correct values to fields including externalid
            clonedCC.name = newMonthlyName(currentCC.name);
            clonedCC.isActive = false;
            clonedCC.startDate = nextStartDate;
            clonedCC.endDate = nextEndDate;
            clonedCC.actualCost = null;
            clonedCC.status = 'Planned';
            clonedCC.Parent = null;      
        // check if the parentid is alread in the map of Parent campaigns created
        if (!pcReferenceMap.containsKey(currentCC.ParentId)) {
            // If not add to list and create new refrence to a Parent Campaign
            Campaign campaignReference = new Campaign(
                ExternalId__c = todayDT.format('yyyyMMdd') + String.valueOf(sequentialNum));                
            clonedCC.Parent = campaignReference;
            pcReferenceMap.put(currentCC.ParentId, campaignReference);
        } else {
            clonedCC.Parent = pcReferenceMap.get(currentCC.ParentId);
    for(Campaign currentPC : currentPCList){
        Campaign clonedPC = currentPC.clone(false,true,true,true);
            clonedPC.name = newYearlyName(currentPC.name);
            clonedPC.isActive = false;
            clonedPC.startDate = begOfYear.addYears(1);
            clonedPC.endDate = endOfYear.addYears(1);
            clonedPC.actualCost = null;
            clonedPC.status = 'Planned';
            clonedPC.ExternalId__c = pcReferenceMap.get(currentPC.id).ExternalId__c;
    // Check and insert cloned parent campaigns
        Database.SaveResult[] results = Database.insert(insertSObjectList);

Here is my current error as well:

System.DmlException: Insert failed. First exception on row 4; first error: INVALID_FIELD, Cannot specify both an external ID reference Parent and a salesforce id, ParentId: []
  • Why do you need the insert to be a single statement?
    – identigral
    Commented Sep 21, 2023 at 23:50
  • I didnt. I ended up just using two inserts as normal. Just wanted to see if it could be done with the same SObjecttype.
    – JEbert
    Commented Sep 26, 2023 at 20:44


You must log in to answer this question.

Browse other questions tagged .