Fun with the CRM StringMap
Ever wonder how (or where) CRM stores the related values for picklists? The answer is in the Stringmap table. Unfortunately, there's not a quick and easy method for looking at and comparing picklist values in CRM - you're pretty much stuck with the In this post, I'll dig a little bit into the structure of the table and how you can query some useful info from it. I will be covering versions 3.0 and 4.0 of Microsoft CRM.
Note: I will not demonstrate and I do not recommend modifying data in this table. You could cause some nasty things to happen with your data if you do. Sticking with the CRM Entity Customization interface is the only safe (and smart) way to go when changing values that are stored in the stringmap.
Table Structure
The structure of the stringmap table is the same for both versions of CRM:
Field | Data Type | Comments |
ObjectTypeCode | INT | Object Type Code of the entity for which the attribute belongs. |
AttributeName | NVARCHAR(100) | Schema name of the picklist attribute. |
AttributeValue | INT | Integer value of the picklist option. This is the value that gets stored in the base tables. |
LangId | INT | Language Code for the CRM deployment. Usually 1033 for English (United States) |
OrganizationId | UNIQUEIDENTIFIER | GUID of the owning Organization. |
Value | NVARCHAR(255) | The actual value that is displayed in the picklist on a form. |
DisplayOrder | INT | Specifies the order in which the value is in the picklist |
VersionNumber | TIMESTAMP | Timestamp for determining the version of the record (when it was last updated). This is used by the synchronization process. |
StringMapId | UNIQUEIDENTIFIER | Primary Key (GUID) for the record. |
The fields that are most often queried are the ObjectTypeCode, AttributeName, AttributeValue and Value. Because I'm not real big on memorizing the EntityTypeCode for all the entities in CRM, I like to link to the metadata and actually see the entity name. Depending on the version of CRM you have, this will be a little different:
- For 3.0, the Object Type Codes and their respective entities can be found in the Entity table in the CRM METABASE.
- For 4.0, the Object Type Codes are found in the MetadataSchema.Entity table in the MSCRM database (4.0 does away with the separate METABASE and rolls the functionality into MetadataSchema.* tables in the MSCRM database).
Queries
Here are a couple of the query snippets that I have saved. This first query just returns a list of each picklist value:
/* CRM 3.0 */
SELECT e.Name, sm.AttribueName, sm.AttributeValue, sm.Value, sm.DisplayOrder
FROM Stringmap sm INNER JOIN <METABASE>.dbo.Entity e ON
sm.ObjectTypeCode = e.ObjectTypeCode
ORDER BY sm.ObjectTypeCode, sm.AttributeName, sm.AttributeValue/* CRM 4.0 */
SELECT e.Name, sm.AttribueName, sm.AttributeValue, sm.Value, sm.DisplayOrder
FROM Stringmap sm INNER JOIN MetadataSchema.Entity e ON
sm.ObjectTypeCode = e.ObjectTypeCode
ORDER BY sm.ObjectTypeCode, sm.AttributeName, sm.AttributeValue
One script that I find useful is this one that compares the same picklists between two different deployments. This assumes you are able to query both environments from the same connection (using linked servers, etc, if they are not on the same database server)
/* CRM 3.0 */
/* QUERYSTRINGMAP 1 */
SELECT t.ObjectTypeCode, e.Name, t.AttributeName, t.AttributeValue, t.Value
INTO #StringMap1
FROM <MSCRM1>.dbo.StringMap t INNER JOIN <METABASE1>.dbo.Entity e ON
t.ObjectTypeCode = e.ObjectTypeCode
ORDER BY t.ObjectTypeCode, t.AttributeName, t.AttributeValue;/* QUERY STRINGMAP 2 */
SELECT p.ObjectTypeCode, e.Name, p.AttributeName, p.AttributeValue, p.Value
INTO #StringMap2
FROM <MSCRM2>.dbo.StringMap p INNER JOIN <METABASE2>.dbo.Entity e ON
p.ObjectTypeCode = e.ObjectTypeCode
ORDER BY p.ObjectTypeCode, p.AttributeName, p.AttributeValue;/* COMBINE QUERIES TO DETERMINE DIFFERENCES */
SELECT t.ObjectTypeCode tObjectTypeCode, t.Name tName, t.AttributeName tAttributeName, t.AttributeValue tAttributeValue, t.Value tValue,
p.ObjectTypeCode pObjectTypeCode, p.Name pName, p.AttributeName pAttributeName, p.AttributeValue pAttributeValue, p.Value pValue
INTO #StringMap_Compare
FROM #StringMap1 t FULL OUTER JOIN #StringMap2 p ON
t.Name = p.Name AND
t.AttributeName = p.AttributeName AND
t.AttributeValue = p.AttributeValue
WHERE (t.ObjectTypeCode IS NULL) OR (p.ObjectTypeCode IS NULL) OR (t.Value <> p.Value)
ORDER BY t.ObjectTypeCode, p.ObjectTypeCode,t.AttributeName, t.AttributeValue,
p.AttributeName, p.AttributeValue;DROP TABLE #StringMap1;
DROP TABLE #StringMap2;
SELECT * FROM #StringMap_Compare;
DROP TABLE #StringMap_Compare;
The script works by querying both stringmaps and placing each result into temp tables, query those tables using a FULL OUTER JOIN to return all records from both tables and putting the result into a third temp table. I like using temp tables heavily in SQL Server because I can keep them around as long as I need them (provided I don't close the originating connection) and then destroy them when I need to. Also, this minimizes reads against live CRM tables which can help avoid potential problems buggy SQL scripts - the damage you do would be against non-production tables. For versio 4.0, the only change needed is to the first two statements:
/* CRM 4.0 */
/* QUERYSTRINGMAP 1 */
SELECT t.ObjectTypeCode, e.Name, t.AttributeName, t.AttributeValue, t.Value
INTO #StringMap1
FROM <MSCRM1>.dbo.StringMap t INNER JOIN <MSCRM1>.MetadataSchema.Entity e ON
t.ObjectTypeCode = e.ObjectTypeCode
ORDER BY t.ObjectTypeCode, t.AttributeName, t.AttributeValue;/* QUERY STRINGMAP 2 */
SELECT p.ObjectTypeCode, e.Name, p.AttributeName, p.AttributeValue, p.Value
INTO #StringMap2
FROM <MSCRM2>.dbo.StringMap t INNER JOIN <MSCRM2>.MetadataSchema.Entity e ON
p.ObjectTypeCode = e.ObjectTypeCode
ORDER BY p.ObjectTypeCode, p.AttributeName, p.AttributeValue;
This code is particularly useful because you can use it to find which picklists exist in one environment versus another and can even compare to see differences in the values. This is pretty important when determining whether two environments are in sync.
I hope this post has given you some helpful hints in querying the underlying CRM tables to glean some information about you particular environment. Enjoy.