Tips for using SQL Server with Salesforce SOQL
Contents
- Simple
SELECT
queries in SQL Server do not work - Bulk inserting data using the SOQL driver
UPDATE
andDELETE
statement bulking- Easysoft transactional support
- Lazy schema validation
- Limitations of Microsoft's OLEDB for ODBC Provider
Simple SELECT
queries in SQL Server do not work
Let's say you are trying to run this query in SQL Server:
SELECT Id FROM SFSOQL8...Account
SQL Server converts that SQL into:
SELECT "Tbl1002"."Id" "Col1004" FROM "Account" "Tbl1002"
As the Salesforce SOQL language does not support the renaming of tables and columns in that way, you end up with this error:
OLE DB provider "MSDASQL" for linked server "SFSOQL8" returned message "[Easysoft][Salesforce SOQL ODBC Driver]General error: Query Failed: 'MALFORMED_QUERY: only aggregate expressions use field aliasing'".
Msg 7320, Level 16, State 2, Line 1
Cannot execute the query "SELECT "Tbl1002"."Id" "Col1004" FROM "Account" "Tbl1002"" against OLE DB provider "MSDASQL" for linked server "SFSOQL8".
SQL Server has two other methods for sending SOQL to the Easysoft driver:
OPENQUERY
, which can be used with local tables to join remote and local data.EXEC
, which can be used with parameters.
To demonstrate these methods, we'll start with a SOQL table join:
SELECT Id, Amount, Name, (SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity
In SQL Server, you could run either of the following queries:
SELECT * FROM OPENQUERY(SFSOQL8, 'SELECT Id, Amount, Name, (SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity')
—Or—
EXEC ('SELECT Id, Amount, Name, (SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity') at SFSOQL8
There is almost no difference in performance as the SOQL you have written is passed straight to the Salesforce server.
Likewise, all the SOQL functions are also available using the same methods:
SELECT * FROM OPENQUERY(SFSOQL8, 'select Id, Name, DISTANCE(CustLocation__c , GEOLOCATION(37.775,-122.418), ''mi'') from Account where Name like ''Bur%'' ')
SELECT * FROM OPENQUERY(SFSOQL8, 'SELECT Type, BillingCountry, GROUPING(Type) grpType, GROUPING(BillingCountry) grpCty, COUNT(id) accts FROM Account GROUP BY CUBE(Type, BillingCountry) ORDER BY GROUPING(Type), GROUPING(BillingCountry)')
If you do pass in invalid SOQL, the Easysoft SOQL driver will return the error directly from Salesforce. For example:
SELECT * FROM OPENQUERY(SFSOQL8, 'SELECT Id, Name, DISTANCE(CustLocation__c , GEOLOCATION(37.775,-122.418), ''mo'') FROM Account WHERE Name LIKE ''Bur%'' ')
OLE DB provider "MSDASQL" for linked server "SFSOQL8" returned message "[Easysoft][Salesforce SOQL ODBC Driver]General error: Query Failed: 'INVALID_FIELD: SELECT Id, Name, DISTANCE(CustLocation__c , GEOLOCATION(37.775 ^ ERROR at Row:1:Column:27 Invalid distance unit: mo. Valid unit: 'mi', 'km''". Msg 7320, Level 16, State 2, Line 1 Cannot execute the query "select Id, Name, DISTANCE(CustLocation__c , GEOLOCATION(37.775,-122.418), 'mo') from Account where Name like 'Bur%' " against OLE DB provider "MSDASQL" for linked server "SFSOQL8".
More information about the SOQL Language can be found here.
Bulk inserting data using the SOQL driver
Within the Salesforce SOAP API, there's a function that allows you to insert up to 200 rows of data from a single SOAP API call. The Easysoft Salesforce SOQL ODBC driver makes use of this function and lets you use SQL Server T-SQL to bulk load up to 200 rows at a time.
In my example, I'm going to add new records to the Account
object within Salesforce. This is a very basic example with only a few columns of data, but I hope this explains how bulk inserts are possible from SQL Server. In SQL Server, I have a local table called Account
which looks like:
begin declare @BlockCount as int declare @IsPosted as int declare @PrmName As nvarchar(255) declare @PrmAddress As nvarchar(255) declare @PrmTown As nvarchar(40) declare @PrmPostCode As nvarchar(30) declare @PrmDescription As nvarchar(255) declare @SQL as nvarchar(255) set @BlockCount=0 set @SQL='INSERT INTO Account (Name, BillingStreet, BillingCity, BillingPostalCode, Description ) VALUES ( ?, ?, ?, ?, ? )' declare select_cursor cursor local FORWARD_ONLY for SELECT AccName, "Property Description", "Address", Town, PostCode FROM Account ORDER BY Id open select_cursor fetch next from select_cursor into @PrmName, @PrmAddress, @PrmTown, @PrmPostCode, @PrmDescription while @@FETCH_STATUS=0 begin if (@BlockCount=0) Begin set @IsPosted=0 exec('Begin Trans') at SFSOQL8 end set @BlockCount=@BlockCount+1 exec (@SQL, @PrmName, @PrmAddress, @PrmTown, @PrmPostCode, @PrmDescription) at SFSOQL8 if (@BlockCount=200) Begin set @IsPosted=1 exec('Commit') at SFSOQL8 end fetch next from select_cursor into @PrmName, @PrmAddress, @PrmTown, @PrmPostCode, @PrmDescription end if (@IsPosted=0) begin exec('Commit') at SFSOQL8 end close select_cursor; deallocate select_cursor; end
This T-SQL works by reading my local Account
table into a cursor.
At the start of the first block of 200 rows, the Begin Trans
is called, which tells the Easysoft driver that any data passed to it will be held until either a COMMIT
or a ROLLBACK
is called.
The EXEC
function sends each row found in the cursor to the Easysoft driver. The driver then builds up the required SOAP API call. When 200 rows have been sent to Easysoft, I send a COMMIT
, which causes the driver to send the SOAP API call to Salesforce.
When the end of the cursor is reached, if there are any records sent to the Easysoft driver that have not been passed to Salesforce, I send a final COMMIT
. The cursor is then closed and deallocated.
The 200 row limit is an internal limit within Salesforce. If you try to send more than 200 rows you will get a Salesforce error. The Easysoft driver has no built in limits, so if Salesforce increase the 200 row limit in future versions of the SOAP API, the Easysoft driver will automatically work with the new limit.
When using this bulk insert method there is no limit to the number of tables you can insert data to in a single block, so the following T-SQL will work:
Begin exec('Begin Trans') at SFSOQL8 exec('INSERT INTO Account ( Name ) VALUES (''Richard Test'') ') AT SFSOQL8 exec('INSERT INTO Opportunity ( Name, StageName, CloseDate ) VALUES (''Richard Test'', ''My Stage'', ''2018-08-14T10:07:00Z'') ') at SFSOQL8 exec('Commit') at SFSOQL8 End
When using the Begin Trans Commit
method for inserts you can only send INSERT
statements. You cannot mix and match inserts and updates as this is not supported in the Salesforce SOAP API.
UPDATE
and DELETE
statement bulking
Our SOQL driver has a built in method for bulking data transactions, when used with an UPDATE
or a DELETE
statement.
This example shows how I update a custom column to a set value.
EXEC ('UPDATE Account SET CustomerPriority__c=''low'' WHERE CustomerPriority__c=NULL') AT SFSOQL8
Salesforce does not support UPDATE
s or DELETE
s using SOQL, so the Easysoft driver has to convert the query into a SOQL SELECT
. The rows returned from the SELECT
are then bulked up into blocks of up to 200 and sent a block at a time for an UPDATE
or DELETE
statement.
Easysoft transactional support
The Easysoft Salesforce SOQL ODBC driver supports a single level of transaction only. So a Begin Trans
followed by some INSERT
s can be sent to Salesforce with a COMMIT
or thrown away within the driver by using a ROLLBACK
.
Lazy schema validation
In your SQL Server linked server properties under the Server Options section is an option for Lazy Schema Validation. By default, this is set to FALSE which causes SQL Server when running a SELECT
statement to send the statement twice. The first time it is sent SQL Server uses the details passed back to build up the metadata about your result set. Then the query is sent again. This is quite an expensive overhead, so we recommend that you set Lazy Schema Validation to TRUE, which means that only one SELECT is sent, which gets both the metadata and the data. This also reduces the number of Salesforce API calls being made.
Limitations of Microsoft's OLEDB for ODBC Provider
Details about the limitations of the OLEDB for ODBC Provider can be found on here:
https://msdn.microsoft.com/en-us/library/ms719628(v=vs.85).aspx