Today's post is written by Rob Jasinski, Database Architect and DBA at Sonoma Partners.
The ability to create custom SSRS reports for Dynamics On-Premise is a very powerful feature and adds a lot of flexibility to CRM’s reporting ability.
However, with that power and flexibility comes the potential to create reports that can run slow, are inefficient, and in worst cases, actually affect the performance of CRM as a whole. This article will focus on some things to watch out for when creating custom SSRS reports for Dynamics CRM On-Premise. Part 2 in this series will focus on Dynamics CRM On-Line.
Use the numeric value for option set values
For option sets in CRM, filtered views return 2 values; the integer value and the name value. For example, StateCode and StateCodeName from FilteredAccount. When filtering by these values, consider using the integer value rather than the name. The integer value is stored directly in the entity table whereas the name value is derived from the stringmap table. In most cases, the numeric value is also indexed and since it’s on the primary table, will perform much better than referencing the derived name.
select name, statecodename from FilteredAccount where statecode = 0
Filtering by date
Use the “utc” version of the date field when filtering by date. Every date field in the CRM filtered views returns 2 fields. For example, actualstart on FilteredAppointment is returned as actualstart and actualstartutc. The actualstart field is actually a calculated field that converts the date to the running user’s local time zone, whereas the actualstartutc field is the raw UTC date retrieved directly from the database table. Since the “utc” version is the actual field in the table, it will leverage any table indexes and perform significantly faster in most cases.
select subject, actualstart from FilteredAppointment where actualstartutc >= '1/1/2015’
Consider using nolock
Whenever data is queried from the database, SQL Server internally manages locks on that query. In a report query that takes a significant time to run, it can prevent the data being updated from other users causing CRM to appear to lock up for other users. One solution is to use the “nolock” query hint. This tells SQL Server to not manage locks for this query, thereby not blocking access to other users who need to change the data. Keep in mind, this also allows “dirty reads”, in that there is a possibility that either the data has changed since the query first started or the original data read is no longer valid. When returning financial or other sensitive data, nolock should not be used. But for non-sensitive reports (i.e. list of open activities, list of accounts, aging reports, etc.), this can help maintain CRM usability and make CRM less susceptible to poor query performance.
select name, accountnumber from FilteredAccount(nolock)
Use Union All versus Union
In more complex reporting queries, you may find the need to union several result sets together to get the data needed for the report. If the result sets will not have any duplicate data across each set, use “Union All” which essentially just concatenates each result set and returns it directly to the report. However “Union” actually collects all the results first, sorts them, then removes any duplicates before returning the results to the report. This can be very time consuming when returning a large number of results.
Avoid “Select *”
Only return the fields the report needs and never use “Select *” when returning data from reports. There are many calculated fields returned from filtered views, from dates converting to local times and currencies converting to local currency. And if these fields are not required for the report, it will still make the calculation for every one of these fields for every row returned. This will take up server resources and cause the query to return data much more slowly. Also, only specifying the fields needed will reduce the amount of network traffic and make the results get back to the report much quicker.
Primary attribute values
Don’t forget that primary attribute values (usually [customprefix]_name) for most lookups are already included in the primary filtered view for the entity being queried. For example, for FilteredContact, the attribute AccountID is a lookup to Contact. The primary attribute on Contact is Name. So the filtered view, FilteredContact, also has a field called AccountIDName, which is the Name value from the associated Contact record. There is no need to join FilteredAccount into the query unless you need additional information outside of the primary attribute.
select FullName, AccountIdName from FilteredContact
Versus
select FullName, Account.Name
from
FilteredContact Contact
join FilteredAccount Account on Contact.AccountId = Account.AccountID
If you have any questions about creating custom SSRS reports for Dynamics CRM On-Premise, please contact us.