3. Left and Right Justification
The Dynamics AX application runtime provides support for left and right justification of fields of type string. By default, string fields are left-justified, and values are stored without modification in the database. If a string
field is right-justified, however, the value is prefixed with enough
blanks when inserted into the database that all available space in the
field is used. When values from a right-justified field are selected
from the database, the application runtime removes the blanks. The
application logic doesn’t know whether a field is right-justified or left-justified because both left-justified and right-justified fields appear the same when used in the X++ application code.
When the application runtime formulates WHERE
clauses in DML statements, it must determine whether fields are
left-justified or right-justified because it adds extra blanks to a
search value when searching for values equal to, lower than, higher
than, and not equal to a field in the database. The application runtime
adds extra blanks to the variable in a statement like the following
when passing the statement to the database. In the following statement,
assume that the accountNum field is right-justified.
select custTable where custTable.accountNum == '4000'
|
The statement passed to the database looks like this.
SELECT ... FROM CUSTTABLE A WHERE A.ACCOUNTNUM = ' 4000'
|
But if the search condition contains wildcard characters, as in the following X++ select statement, the application runtime must remove the blanks from the field being searched by applying LTRIM to the statement.
select custTable where custTable.accountNum like '4%';
|
This code produces the expected result of selecting all custTable records where the accountNum field starts with ‘4’, and the preceding X++ statement produces a statement like the following.
SELECT ... FROM CUSTTABLE A WHERE LTRIM(A.ACCOUNTNUM) LIKE '4%'
|
The introduction of the LTRIM function in the WHERE clause prevents both of the supported databases from searching in an index for the value in accountNum, which could have a severe effect on the performance of the statement.
Note
None
of the preceding SQL statements is a clear match to the statement
passed to either of the databases; they are intended to serve as
examples only. The application runtime applies some additional
functions when the LIKE operator is used. |
The application runtime also applies LTRIM if a right-justified field is compared with a left-justified field. In the following select statement written in X++, assume that accountNum is right-justified and accountRelation is left-justified.
select priceDiscTable notexists join custTable where priceDiscTable.accountRelation == custTable.accountNum
|
The statement passed to the database wraps the right-justified column in an LTRIM function, and it looks like this.
SELECT ... FROM PRICEDISCTABLE A WHERE NOT EXISTS (SELECT 'x' FROM CUSTTABLE B WHERE A.ACCOUNTRELATION=LTRIM(B.ACCOUNTNUM))
|
As
mentioned earlier, this behavior could have a severe effect on
performance, so you should decide whether this possible degradation of
performance is acceptable before you change a field from left to right
justification.
4. Placeholders and Literals
The
database layer in the Dynamics AX application runtime formulates SQL
statements containing either placeholders or literals—that is,
variables or constants. Whether the application runtime chooses to use
placeholders instead of literals has nothing to do with using variables
or constants when the statements are formulated in either X++ or the
application runtime. The following X++ select statement, which selects
the minimum price for a given customer, contains constants and a
variable.
select minof(amount) from priceDiscTable where priceDiscTable.Relation == PriceType::PriceSales && priceDiscTable.AccountCode == TableGroupAll::Table && priceDiscTable.AccountRelation == custAccount
|
The statement is passed to the SQL Server 2005 database when placeholders are used, as shown here.
SELECT MIN(A.AMOUNT) FROM PRICEDISCTABLE A WHERE DATAAREAID=@P1 AND RELATION=@P2 AND ACCOUNTCODE=@P3 AND ACCOUNTRELATION=@P4
|
The statement is passed as follows when literals are used. Assume that the statement is executed in the ‘dat’ company and that you are searching for the lowest price for customer ‘4000’.
SELECT MIN(A.AMOUNT) FROM PRICEDISCTABLE A WHERE DATAAREAID=N'dat' AND RELATION=4 AND ACCOUNTCODE=0 AND ACCOUNTRELATION=N'4000'
|
As
you can see, the use of constants or variables in the formulation of
the statement in X++ has no effect on the use of placeholders or
literals when the SQL statement is formulated. However, using join or specific keywords in the statement when formulating the statement in X++ does have an effect.
The
default behavior of Dynamics AX is to use placeholders, but if the
Microsoft Dynamics AX Server Configuration Utility option Use Literals
In Complex Joins From X++ is selected, statements containing joins use
literals if the application runtime considers the statement to be a
complex join. The application runtime determines that a join is complex
if the statement contains two or more tables associated with the
following table groups: Main, WorksheetHeader, WorksheetLine,
Transaction, and Miscellaneous. Tables associated with the Group and
Parameter table groups are not included when determining whether the
join is complex.
Note
The
SYS layer in Dynamics AX contains approximately 2100 ordinary tables,
and about 700 of these are associated with the Group and Parameter
table groups. |
Figure 2 shows an example of the TableGroup property in the list of metadata properties for a table.

Note
The
Server Configuration Utility option Use Literals In Complex Joins From
X++ is selected by default when you install Axapta 3.0 and cleared when
you install or upgrade to Dynamics AX 2009. |
The
difference between using placeholders and literals lies mainly in the
ability of the database to reuse execution plans and the accuracy of
the calculated execution plan. When literals are used in a statement,
the query optimizer in the database knows the exact values being
searched for and can therefore use its statistics more accurately; when
placeholders are used, the optimizer doesn’t know the values. But
because the execution plan is based on the exact values when literals
are used, it can’t be reused when the same statement is passed again
with different search values. Placeholders do allow reuse of the
execution plan. Whether placeholders or literals result in the best
performance depends on three factors:
How often the same statement is executed with different values
How much better the query optimizer is at calculating the optimal execution plan when the exact values are known
The total time required to execute the actual statement
Usually,
both approaches result in similar execution plans; placeholders are
generally preferred because execution plans can be reused, which
results in better performance overall.
You can explicitly state that a join
statement should always use placeholders when the SQL statement is
formulated by the application runtime, regardless of the table group
settings on the tables in the statement and the Server Configuration
Utility options. You do this by adding the forceplaceholders keyword to the statement in X++, as shown in the following select statement (which would use literals if the previously mentioned Server Configuration Utility option were selected).
select forceplaceholders priceDiscTable notexists join custTable where priceDiscTable.accountRelation == custTable.accountNum
|
The alternate keyword forceliterals
is also available in X++. This keyword explicitly causes literals to be
used when the application runtime formulates the SQL statements.
Tip
The
Query framework also allows you to explicitly state whether
placeholders or literals should be used for a given query by calling query.literals(1)query.literals(2) to enforce placeholders, and query.literals(0)
to let the application runtime decide which to use. Unfortunately, no
enumeration is available from the Dynamics AX application runtime to
use in place of these integer constants, but the macros #QueryLiteralsDefault, #QueryForceLiterals, and #QueryForcePlaceholders are available from the Query macro library. to enforce literals, |