In a copy of Salesforce using EnrollmentRx, we are capturing the details of every submission-from-a-student on a table attached to "Contact" (PK-FK) called "Touch_Point__c."
When such a "Touch_Point__c" record is created, if it is the first created for a given "Contact," a trigger copies its "Lead_Source__c" value over to the corresponding "Contact" record's "LeadSource" field.
Midway through an advertising campaign, a decision was made to change the string used for a certain departmental landing page's "Lead_Source__c" value from "Normal Welcome Page" to "Landing Page."
We'd caught up on back-filling "Lead_Source__c" values on old "Touch_Point__c" table records.
However, we hadn't yet back-filled the corresponding "LeadSource" fields on "Contact" in the case where such "Touch_Point__c" records had been the first in existence for a given "Contact."
(We wanted to leave "Contact" records alone where none of the altered-after-the-fact "Touch_Point__c" were actually the first "Touch_Point__c" record for the "Contact.")
I wrote a little script that's the equivalent of a complex "UPDATE" statement and am sharing it here for colleagues from the Oracle SQL world.
(Please excuse any typos or inefficiencies -- the real data set was small, so I didn't care about performance, and I didn't actually run the Oracle.)
Here's some Oracle SQL that I believe would've done the job, if Salesforce were a normal Oracle database:
UPDATE Contact SET LeadSource = ( SELECT Lead_Source__c FROM Touch_Point__c INNER JOIN ( SELECT Contact__c, MIN(CreatedDate) AS MIN_CR_DT FROM Touch_Point__c GROUP BY Contact__c ) qEarliestTP ON Touch_Point__c.Contact__c = qEarliestTP.Contact__c AND Touch_Point__c.CreatedDate = qEarliestTP.MIN_CR_DT WHERE Contact.Id = Touch_Point__c.Contact__c AND Lead_Source__c='Landing Page' AND Dept_Name__c='Math' AND utm_source__c is not null AND extract(year from CreatedDate) >= extract(year from current_date) ) WHERE Contact.Id IN ( SELECT Touch_Point__c.Contact__c FROM Touch_Point__c INNER JOIN ( SELECT Contact__c, MIN(CreatedDate) AS MIN_CR_DT FROM Touch_Point__c GROUP BY Contact__c ) qEarliestTP ON Touch_Point__c.Contact__c = qEarliestTP.Contact__c AND Touch_Point__c.CreatedDate = qEarliestTP.MIN_CR_DT WHERE Contact.Contact__c = Touch_Point__c.Contact__c AND Lead_Source__c='Landing Page' AND Dept_Name__c='Math' AND utm_source__c is not null AND extract(year from CreatedDate) >= extract(year from current_date) ) AND Contact.LeadSource='Normal Welcome Page'
Here's the Salesforce Apex code (with embedded SOQL) I wrote to do the job instead, since Salesforce doesn't give you a full-on SQL-type language.**
// Loop through every record in the "Touch_Point__c" table, setting it aside in a map, keyed by its foreign key to a the "Contact," if it is the earliest-created "Touch_Point__c" for that Contact Map<Id, Touch_Point__c> cIDsToEarliestTP = new Map<Id, Touch_Point__c>(); List<Touch_Point__c> allTps = [ SELECT Id, Contact__c, CreatedDate FROM Touch_Point__c ORDER BY Contact__c, CreatedDate ASC ]; for (Touch_Point__c tp : allTPs) { // The "ORDER BY" in allTPs should make this logic short-circuit at the first half of the "IF," but 2nd half will dummy-check if the list is, for some reason, out of order. if (!cIDsToEarliestTP.containsKey(tp.Contact__c) || cIDsToEarliestTP.get(tp.Contact__c).CreatedDate > tp.CreatedDate) { cIDsToEarliestTP.put(tp.Contact__c, tp); } } // Loop through every "landing page visit"-typed record in the "Touch_Point__c" table, updating a modified in-memory copy of the record in the "Contact" table it references to a list of "Contact" records called "csToUpdate" ONLY IF the "landing page"-related TouchPoint is also the "earliest-created" TouchPoint for that Contact record -- then call a DML operation on that in-memory list to persist it to the database. List<Contact> csToUpdate = new List<Contact>(); List<Touch_Point__c> mathLandingTPs = [ SELECT Id, Contact__c, Lead_Source__c, Contact__r.LeadSource, Dept_Name__c, utm_source__c, CreatedDate FROM Touch_Point__c WHERE Lead_Source__c='Landing Page' AND Contact__r.LeadSource='Normal Welcome Page' AND Dept_Name__c='Math' AND utm_source__c<>null AND CreatedDate>=THIS_YEAR ]; for (Touch_Point__c tp : mathLandingTPs ) { if (cIDsToEarliestTP.containsKey(tp.Contact__c) && cIDsToEarliestTP.get(tp.Contact__c).Id == tp.Id) { csToUpdate.add(new Contact(Id=tp.Contact__c, LeadSource=tp.Lead_Source__c)); } } UPDATE csToUpdate;
Oracle programmers, I imagine your colleagues might yell at you if you used PL/SQL to hand-iterate over smaller SQL queries in Oracle rather than using native SQL do the work for you. In Salesforce, that's simply the way it's done.
**Note that more complex code might be required -- e.g. you might have to run the same code several times with a row-count limit on it -- since Salesforce is pretty picky about the performance of triggers fired in response to a DML statement. (A normal Oracle database has its limits, too, of course, but they're likely far less strict if it's your own in-house database than with Salesforce.)
No comments:
Post a Comment