Our Salesforce instance loads quite a bit of data via the Data Loader (so much, that it sometimes screws things up). This is done in automated batches via the command line. Better yet, the information is extracted directly from our JDBC database — no disk files are involved! To top it off, it even runs on a Linux system (which was a little difficult because Data Loader is only distributed as a Windows executable!).
We also export some data from Salesforce, again inserting it directly into a database.
My challenge for today was to extract some information from the Opportunity Stage History (kept in the OpportunityHistory
object). Specifically, I wanted to extract some Opportunity data together with the date that the Opportunity’s Stage
was set to ‘Lost’. This required ‘joining’ between the Opportunity
and OpportunityHistory
objects.
I referred to the trusty SOQL Relationship Queries documentation and wound up writing a query on the OpportunityHistory
object that also included data from the ‘parent’ Opportunity
object:
select Opportunity.Reason_Lost__c, -- My custom fields Opportunity.Expiry_date__c, Opportunity.Product_Family__c, CreatedDate -- Date on the Stage History record from OpportunityHistory where StageName = 'Lost' -- On OpportunityHistory and Opportunity.Reason_lost__c != '' and Opportunity.CreatedDate >= 2009-11-01T00:00:00Z -- I always hate having to write dates like this!
This very nicely returned me rows from Opportunity
objects together with the date that the Stage
was changed to ‘Lost’.
However, I had a lot of trouble getting it to load, and then I realised that my SDL file (used to map fields) also had to have the ‘Opportunity.
‘ prefix!
In export_map_reason_lost.sdl
:
Opportunity.Reason_Lost__c=reason_lost Opportunity.Product_Family__c=product Opportunity.Expiry_Date__c=expiry_date CreatedDate=lost_date
At this point, I should give a shout-out to my favourite Salesforce employee, Simon Fell (see my interview with him at Dreamforce 2009). He has written a great Mac utility called Soql Xplorer that makes writing SOQL a snap!
To assist any readers who are trying to nut-out how to use Data Loader to push/pull data to/from a database, here’s the relevant entries in my XML files:
In process-conf.xml
:
<bean id="extractReasonLost" class="com.salesforce.dataloader.process.ProcessRunner" singleton="false"> <description>Extract Lost Opportunities to discover Reason Lost</description> <property name="name" value="extractReasonLostName"/> <property name="configOverrideMap"> <map> <entry key="sfdc.endpoint" value="https://emea.salesforce.com"/> <entry key="sfdc.username" value="john.rotenstein@atlassian.com"/> <entry key="sfdc.password" value="secret"/> <entry key="sfdc.timeoutSecs" value="600"/> <entry key="sfdc.loadBatchSize" value="100"/> <entry key="sfdc.entity" value="Opportunity"/> <entry key="process.enableLastRunOutput" value="false" /> <entry key="sfdc.extractionRequestSize" value="500"/> <entry key="sfdc.extractionSOQL" value="select Opportunity.Reason_Lost__c, Opportunity.Expiry_date__c, Opportunity.Product_Family__c, CreatedDate from OpportunityHistory where StageName = 'Lost' and Opportunity.Reason_lost__c != '' and Opportunity.CreatedDate >= 2009-11-01T00:00:00Z"/> <entry key="process.operation" value="extract"/> <entry key="process.mappingFile" value="export_map_reason_lost.sdl"/> <entry key="dataAccess.type" value="databaseWrite"/> <entry key="dataAccess.name" value="extractReasonLostBean"/> </map> </property> </bean>
In database-conf.xml
:
<bean id="extractReasonLostBean" class="com.salesforce.dataloader.dao.database.DatabaseConfig" singleton="true"> <property name="sqlConfig" ref="extractReasonLostQuery"/> <property name="dataSource" ref="server_name"/> </bean> <bean id="extractReasonLostQuery" class="com.salesforce.dataloader.dao.database.SqlConfig" singleton="true"> <property name="sqlString"> <value> INSERT INTO renewals_lost ( period, reason_lost, expiry_date, product, lost_date) VALUES (@period@::numeric, @reason_lost@, @expiry_date@, @product@, @lost_date@ ) </value> </property> <property name="sqlParams"> <map> <entry key="period" value="integer"/> <entry key="reason_lost" value="java.lang.String"/> <entry key="expiry_date" value="java.sql.Date"/> <entry key="product" value="java.lang.String"/> <entry key="lost_date" value="java.sql.Date"/> </map> </property> </bean>
The Bottom Line
- Data Loader is very powerful for importing and exporting directly to/from a database
- When extracting via SOQL that involves a relationship, include the fully qualified name in the SDL file (eg
Opportunity.Stage
) - Simon Fell is my hero!