Walkthrough: More than 14 InventDim Dimensions Despite Index Limit [AX 2012]
This article concerns the InventDim table. It describes how you can overcome the limit of 14 dimension fields on your inventory items. The following solution shows that, for each record, the values of all extra dimension fields can be combined into one hash value that is stored in one hash field. The hash field is included as the last field in the InventDim.DimIdx table index.
This article uses the terms of primary versus secondary dimensions. Conceptually a primary dimension is more important to your business than a secondary dimension. In the technical implementation, the field for a primary dimension is directly included in the DimIdx index. The field value for a secondary dimension is an input into the hashing algorithm that computes the value for a hash field. The hash field is the last field in the DimIdx index. The field for a secondary dimension is not in DimIdx in any other way.
The solution that is described here first became available in Microsoft Dynamics AX 2012 R2.
The following table describes the elements that work together to create the solution. The elements include AOT items such as indexes, fields, methods, and more.
After the following table, the subsequent section explains the steps you can follow to implement the solution for your own extra dimensions. The steps show how the elements of the solution work together.
Element name |
Element type |
Description | ||
---|---|---|---|---|
InventDim |
Table |
The InventDim table contains values for inventory dimensions. All dimension fields must be represented in the DimIdx index, which is a unique index. Only the primary key field, named inventDimId, is excluded from DimIdx. Some dimension fields are represented in DimIdx in the usual direct manner. However, if there are more than 14 dimension fields, those of secondary importance are represented in the SHA1Hash field. Multiple secondary dimensions can be represented in the single SHA1Hash field. The SHA1Hash field is included in the DimIdx index. |
||
DimIdx |
Table index |
Here are two views of the dimensions index on the InventDim table. This index forbids duplicate values. The underlying SQL Server index is limited to a maximum of 16 fields. The field SHA1Hash is included in DimIdx, as the last field in the index.
|
||
Sha1HashCode |
System data type |
The type that represents a hash value in the AX system. This type can be used for a table field, or for a variable in a method, and so on just as any other type can be used. The extended data type (EDT) named InventDimSHA1Hash, in its Extends property, references this system data type. For more information see AOT > System Documentation > Types. |
||
InventDimSHA1Hash |
Extended data type (EDT) |
A type that extends the system data type named Sha1HashCode. On the InventDim table, the hash field named SHA1Hash is of this type. For more information, see AOT > Data Dictionary > Extended Data Types. |
||
SHA1Hash |
Table field |
A field on the InventDim table, of type InventDimSHA1Hash. This field stores the hashed values of additional InventDim fields above the 14 field limit. The hashValue method computes the value that is then stored in the SHA1Hashfield. |
||
hashKey |
Table method |
A method on the InventDim table. This method is called by the hashValue method. The hashKey method returns a string containing the values of the additional InventDim fields above the 14 field limit. Partners whose customizations need more than 14 dimensions in the InventDim table must edit this method to add their new field names. The edit must be similar to the following X++ code which shows a pretend dimension field named FluffinessQuotientId. The field is added near the end of the hashKey method: public str hashKey() // X++, method on InventDim table. { str hashKey = ''; #InventDimDevelop /** (Large comment removed for brevity.) ... using the code pattern below for each field included in the hash. if (this.<FieldName>) { hashKey += (hashKey ? '~' : '') + '<FieldName>:' + this.<FieldName>; } **/ // Due to index limitations, hash the value of // the PRETEND EXAMPLE extra "dimension field". // if (this.FluffinessQuotientId) { hashKey += (hashKey ? '~' : '') + 'FluffinessQuotientId:' + this.FluffinessQuotientId; } return hashKey; } |
||
hashValue |
Table method |
A method on the InventDim table. This method is called by the insert and update methods. This method calls the hashKey method. The value returned by this method is very likely to be unique for any given set of unique inputs. The value is stored in the SHA1Hash field. |
||
insert |
Table method |
A method that is inherited by each table, and which is overridden on the InventDim table. This method calls the hashValue method, and then stores the result in the SAH1Hash field. |
||
update |
Table method |
A method that is inherited by each table, and which is overridden on the InventDim table. This method calls the hashValue method, and then stores the result in the SAH1Hash field. |
||
findDim |
Table method |
A static method on the InventDim table. The method is used to retrieve a record from the InventDim table. The findDim method has been updated to use the hashing infrastructure described in this document. Therefore any new fields added to the hashed field collection are used to find matching InventDim records. Whenever you modify the schema of dimension fields on the InventDim table, you must modify the findDim method to concur with the modification. Any new dimensions that you add to the hashKey, what we call secondary dimensions, have no effect on the code in the findDim method. But if you add a primary dimension field to the InventDimtable, you must also add the field to both long lists of dimensions that are in the code of the findDim method. For example, in the following X++ code, the pretend dimension field named FluffinexxQuotientId has been added as a primary. client server static public InventDim findDim( InventDim _inventDim, boolean _forupdate = false) { // <GEERU> #ISOCountryRegionCodes // </GEERU> InventDim inventDim; if (_forupdate) { inventDim.selectForUpdate(_forupdate); } // Fields might not have been selected on the specified // buffers, or might have been updated since selection _inventDim.checkInvalidFieldAccess(false); if (isConfigurationkeyEnabled(configurationKeyNum(InventDimExtensibility))) { select firstonly inventDim where inventDim.ConfigId == _inventDim.ConfigId && inventDim.InventSizeId == _inventDim.InventSizeId && inventDim.InventColorId == _inventDim.InventColorId && inventDim.InventStyleId == _inventDim.InventStyleId && inventDim.InventSiteId == _inventDim.InventSiteId && inventDim.InventLocationId == _inventDim.InventLocationId && inventDim.InventBatchId == _inventDim.InventBatchId && inventDim.wmsLocationId == _inventDim.wmsLocationId && inventDim.wmsPalletId == _inventDim.wmsPalletId // Pretend example added primary dimension. && inventDim.FluffinessQuotientId == _inventDim.FluffinessQuotientId && inventDim.sha1Hash == _inventDim.hashValue() // Needed to hit unique index cache. All dimensions should // be included in the where clause - also those included // in the hash key, && inventDim.InventSerialId == _inventDim.InventSerialId && inventDim.InventGtdId_RU == _inventDim.InventGtdId_RU && inventDim.InventProfileId_RU == _inventDim.InventProfileId_RU // Demoting InventOwnerId_RU to secondary, commenting it out. //&& inventDim.InventOwnerId_RU == _inventDim.InventOwnerId_RU; } else { select firstonly inventDim where inventDim.ConfigId == _inventDim.ConfigId && inventDim.InventSizeId == _inventDim.InventSizeId && inventDim.InventColorId == _inventDim.InventColorId && inventDim.InventStyleId == _inventDim.InventStyleId && inventDim.InventSiteId == _inventDim.InventSiteId && inventDim.InventLocationId == _inventDim.InventLocationId && inventDim.InventBatchId == _inventDim.InventBatchId && inventDim.wmsLocationId == _inventDim.wmsLocationId && inventDim.wmsPalletId == _inventDim.wmsPalletId // Pretend example of added primary dimension. && inventDim.FluffinessQuotientId == _inventDim.FluffinessQuotientId // <GEERU> && inventDim.InventGtdId_RU == _inventDim.InventGtdId_RU && inventDim.InventProfileId_RU == _inventDim.InventProfileId_RU // Demoting InventOwnerId_RU to secondary, commenting it out. //&& inventDim.InventOwnerId_RU == _inventDim.InventOwnerId_RU // </GEERU> && inventDim.InventSerialId == _inventDim.InventSerialId; } #inventDimDevelop return inventDim; } |
First plan, then implement.
It is simpler to add a secondary dimension. For the following steps it is assumed that you want the new dimension to be a primary dimension, and that it must displace another dimension that has been primary until now.
-
Decide whether you want your new dimension field to be among the 14 most important, or instead one of secondary importance.
For this example you add a primary dimension field that you name FluffinessQuotientId. -
Decide which primary dimension field you are demoting to secondary.
For this example you demote the InventOwnerId_RU field.
-
Add your new dimension field named FluffinessQuotientId to the InventDim table.
-
Edit the InventDim.hashKey method to add another if(){} block for the secondary dimension field, which in this example is the InventOwnerId_RU field.
Simply match the pattern that is documented by comments in the hashKey method. Also, the example code in the earlier table, in the row for hashKey, shows the code that you must add for theFluffinessQuotientId field. -
Modify the InventDim.DimIdx index to remove the dimension field InventOwnerId_RU which you are demoting to secondary.
For information about indexes in the AOT, see How to: Create an Index.
-
Further modify the DimIdx index to add your new primary dimension field FluffinessQuotientId.
-
Update the InventDim::findDim method to add your new dimension FluffinessQuotientId to the select statement.
The necessary code is shown in the earlier table, in the row for findDim. -
Also in findDim, remove the InventOwnerId_RU field. This field now affects the value returned by the hashValue method.
-
Modify any SQL statements whose speed performance relies on the demoted field InventOwnerId_RU being a primary field of the DimIdx index.
The cross-reference feature can tell you where a given field is referenced in X++ SQL code anywhere in your AX system. For more information, see How to: Create the Cross-Reference System.
Or consider creating a new index for the secondary dimension field.
-
If your system needs exactly 14 dimensions, you can consider removing the SHA1Hash field from the DimIdx index. Later if you need a total of 15 or more dimensions, you would have to add theSHA1Hash field back as the last field in the DimIdx index.
-
Any X++ SQL statements which insert or update data in the InventDim table, but which avoid the insert and update methods on the InventDim table, will corrupt the data in the computedSHA1Hash field.
-
For better performance, it might be good to add another index for each secondary dimension field. The details depend on your particular implementation.
-
No set-based SQL operations can be used for insert or update of the InventDim table. The insert and update methods on the InventDim table handle only single-record operations.
-
To see an example of a form that might display some dimension related data, view the following form:
Form: InventJournalTable,
at Menu Item: InventJournalTableMovement,
at path: Inventory and warehouse management > Journals > Item transactions > *
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix