Translate

Sunday, April 3, 2011

Query Ranges in X++

Expressions in query ranges
One of least understood but most powerful Axapta features is the so-called Expressions in query ranges syntax. This is not the same as simply using a QueryBuildRange object in a query and specifying a criteria for a single field.

Introduction

This is a method of specifying ranges on queries which allows you to perform complex comparisons, and create complex join situations which would be impossible using the standard syntax.


Syntax

To use the special syntax, you should first add a range to your QueryBuildDataSource object in the normal way. Note that for this special syntax, it does not matter which field you use to add the range.
To specify the range value itself, certain rules must be followed:
§  The entire expression must be enclosed within single-quotes, not double-quotes
§  The entire expression must be enclosed in parenthesis (brackets)
§  Each sub-expression must be enclosed in its own set of parenthesis
§  For fields in the current table, simply the field name can be used
§  For fields in other tables, a prefix of the relevant datasource name must be added. This is not always the same as the table name.
§  String values should be surrounded by double-quotes, and wrapped in a call to queryValue()
§  Enum values should be specified by their integer value
§  Date values should be formatted using Date2StrXpp()
§  Blank string like ' ' will not work as expected, use sysquery::valueEmptyString().


Examples

In the example below, we construct a query and add a single datasource.
The range is then added, using the DataAreaId field on each table. Any field can be used, but using an unusual one such as DataAreaId helps remind a casual reader of the code that it's not a normal range.

query = new Query();
dsInventTable = query.addDataSource(tableNum(InventTable));
 
// Add our range
queryBuildRange = dsInventTable.addRange(fieldNum(InventTable, DataAreaId));
Given the above, the following are valid range specifications:

 

Simple criteria

Find the record where ItemId is B-R14. Take note of the single quotes and parenthesis surrounding the entire expression.
queryBuildRange.value(strFmt('(ItemId == "%1")', queryValue("B-R14")));
Find records where the ItemType is Service. Note the use of any2int().
queryBuildRange.value(strFmt('(ItemType == %1)', any2int(ItemType::Service)));
Find records where the ItemType is Service or the ItemId is B-R14. Note the nesting of the parenthesis in this example.
queryBuildRange.value(strFmt('((ItemType == %1) || (ItemId == "%2"))', 
    any2int(ItemType::Service),
    queryValue("B-R14")));
Find records where the modified date is after 1st January 2000. Note the use of Date2StrXpp() to format the date correctly.
queryBuildRange.value(strFmt('(ModifiedDate > %1)', Date2StrXpp(01012000)));
Find records where the Field is blank (null) or an empty string. For more see Sys::Query Docs
qbrStatement = this.query().dataSourceName("BankAccountTrans2").addRange(fieldnum(BankAccountTrans,AccountStatement));
//qbrStatement.value("!?*");//this is the old way that may not work in future versions of AX
qbrStatement.value(sysquery::valueEmptyString());//this is the new way 

 

Complex criteria with combined AND and OR clauses

Find all records where the ItemType is Service, or both the ItemType is Item and the ProjCategoryId is Spares. This is not possible to achieve using the standard range syntax.
Note also that in this example, we are using the fieldStr() method to specify our actual field names and again, that we have nested our parenthesis for each sub-expression.
queryBuildRange.value(strFmt('((%1 == %2) || ((%1 == %3) && (%4 == "%5")))',
    fieldStr(InventTable, ItemType),
    any2int(ItemType::Service),
    any2int(ItemType::Item),
    fieldStr(InventTable, ProjCategoryId),
    queryValue("Spares")));

 

WHERE clauses referencing fields from multiple tables

For this example below, we construct a query consisting of two joined datasources (using an Exists join). Note that we specify the datasource names when adding the datasources to the query.
The ranges are then added, using the DataAreaId field on each table as described in the earlier example.
query = new Query();
dsInventTable = query.addDataSource(tableNum(InventTable), tableStr(InventTable));
dsInventItemBarCode = dsInventTable.addDataSource(tableNum(InventItemBarCode), tableStr(InventItemBarCode));
dsInventItemBarCode.relations(true);
dsInventItemBarCode.joinMode(JoinMode::ExistsJoin);
 
// Add our two ranges
queryBuildRange1 = dsInventTable.addRange(fieldNum(InventTable, DataAreaId));
queryBuildRange2 = dsInventItemBarCode.addRange(fieldNum(InventItemBarCode, DataAreaId));
Find all records where a bar code record exists for an item and was modified later than the item was modified.
In this example, we are using the range on the BarCode table. Therefore the unqualified ModifiedDate reference will relate to InventItemBarCode.ModifiedDate. The other field is a fully-qualified one, using the DatasourceName.FieldName syntax.
queryBuildRange2.value(strFmt('(ModifiedDate > InventTable.ModifiedDate)'));
Note that if we had added our InventTable datasource using the following code
dsInventTable = query.addDataSource(tableNum(InventTable), "InventTableCustomName"); // Note that we are manually specifying a different datasource name 
then the query range would need to appear as follows
queryBuildRange2.value(strFmt('(ModifiedDate > InventTableCustomName.ModifiedDate)'));

 

Conditional joins

We will modify our previous example slightly, to remove the automatic addition of relations for the join.
query = new Query();
dsInventTable = query.addDataSource(tableNum(InventTable), "InventTable");
dsInventItemBarCode = dsInventTable.addDataSource(tableNum(InventItemBarCode), "InventItemBarCode");
dsInventItemBarCode.joinMode(JoinMode::ExistsJoin);
 
// Add our two ranges
queryBuildRange1 = dsInventTable.addRange(fieldNum(InventTable, DataAreaId));
queryBuildRange2 = dsInventItemBarCode.addRange(fieldNum(InventItemBarCode, DataAreaId));
We can now use the query expression to specify whatever we like as the join criteria.
Find all records where either the ItemType is Service, or the ItemType is Item and a barcode exists. The join criteria is only applied in the second half of the expression, so all Service items will appear irrespective of whether they have a bar code. Again, this is not possible to achieve using the standard query ranges.
queryBuildRange2.value(strFmt('((%1.%2 == %3) || ((%1.%2 == %4) && (%1.%5 == %6)))',
    query.dataSourceTable(tableNum(InventTable)).name(), // InventTable %1
    fieldStr(InventTable, ItemType), // ItemType %2
    any2int(ItemType::Service), // %3
    any2int(ItemType::Item), // %4
    fieldStr(InventTable, ItemId), // ItemId %5
    fieldStr(InventItemBarCode, ItemId))); // %6 
Using the techniques above, it is possible to create queries with almost as much flexibility as using SQL statements directly.
Filter on array fields
queryBuildRange.value(strFmt('((%1.%2 == "%4") || (%1.%3 == "%5"))', 
    queryBuildDataSource.name(),
    fieldid2name(tablenum(<table>), fieldid2ext(fieldnum(<table>, Dimension), Dimensions::code2ArrayIdx(SysDimension::Center))), 
    fieldid2name(tablenum(<table>), fieldid2ext(fieldnum(<table>, Dimension), Dimensions::code2ArrayIdx(SysDimension::Purpose))), 
    "some dim2 value", 
    "some dim3 value"));
Note: you must always specify the datasource name if you use Query Expression syntax to filter on array fields. See also Limitations section at the bottom of the page.

 

Using wildcards and comma-separated range values

Again, the previous example here was using standard syntax, not the special syntax using expressions. It's not possible to modify the above examples to work with wildcards.
The above statement applies to AX versions < 5.0
AX 5.0 introduced solution to wildcards - while you still cannot directly use wildcards in ranges, now it supports the 'LIKE' keyword.
(AccountNum LIKE "*AA*" || Name LIKE "*AA*")

10 comments:

  1. Hi Mukesh,
    I have added a new field of enumtype NoYes in InventItemGroup table. In one of the form lookup, I want only those item ids with the newly added field value as Yes. This lookup is displaying item ids from InventTable i.e., it is displaying all item ids.
    Can u pls suggest the code to be written in the lookup method for the requirement?

    Thanks in advance.

    ReplyDelete
  2. Hi,

    You need to join InventTable with InventItemGroup table :

    query = new Query();
    dsInventTable = query.addDataSource(tableNum(InventTable), tableStr(InventTable));
    dsInventItemGroup = dsInventTable.addDataSource(tableNum(InventItemGroup), tableStr(InventItemGroup));
    dsInventItemGroup.relations(true);
    dsInventItemGroup.joinMode(JoinMode::InnerJoin);

    and provide a range on your new field and pass value as queryValue(NoYes::Yes) it will work.

    ReplyDelete
    Replies
    1. But InventTable and InventItemGroup tables don't have any common field between them. So I don't think this will work. Instead I have written this code:

      queryBuildDataSourceInventTable = query.addDataSource(tableNum(InventTable));
      queryBuildDataSourceInventItemGroupItem = queryBuildDataSourceInventTable.addDataSource((tableNum(InventItemGroupItem)));
      queryBuildDataSourceInventItemGroupItem.relations(true);
      queryBuildDataSourceInventItemGroupItem.joinMode(JoinMode::ExistsJoin);

      queryBuildDataSourceInventItemGroup = queryBuildDataSourceInventItemGroupItem.addDataSource(tableNum(InventItemGroup));
      queryBuildDataSourceInventItemGroup.relations(true);
      queryBuildDataSourceInventItemGroup.joinMode(JoinMode::ExistsJoin);

      queryBuildRange = queryBuildDataSourceInventItemGroup.addRange(fieldNum(InventItemGroup, Enable));
      queryBuildRange.value(SysQuery::value(NoYes::Yes));


      Please check if this code is fine.
      Thanks.

      Delete
  3. Hi Yunus,
    Hope you have solved this. But I am writing this for others who want to add join using Query.

    queryBuildDataSourceInventTable = query.addDataSource(tableNum(InventTable));
    queryBuildDataSourceInventItemGroupItem = queryBuildDataSourceInventTable.addDataSource((tableNum(InventItemGroupItem)));
    queryBuildDataSourceInventItemGroupItem.relations(true);
    queryBuildDataSourceInventItemGroupItem.joinMode(JoinMode::ExistsJoin);

    queryBuildDataSourceInventItemGroup = queryBuildDataSourceInventItemGroupItem.addDataSource(tableNum(InventItemGroup));
    queryBuildDataSourceInventItemGroup.relations(false);
    queryBuildDataSourceInventItemGroup.joinMode(JoinMode::ExistsJoin);
    queryBuildDataSourceInventItemGroup.addLink(fielNum(InventTable, ItemId), fieldNum(InventItemGroupItem, ItemId));
    queryBuildDataSourceInventItemGroup.addLink(fielNum(InventTable, DataAreaId), fieldNum(InventItemGroupItem, DataAreaId));
    queryBuildRange = queryBuildDataSourceInventItemGroup.addRange(fieldNum(InventItemGroup, Enable));
    queryBuildRange.value(SysQuery::value(NoYes::Yes));

    It will work. Try it

    ReplyDelete
  4. Консоли от компании Microsoft не сразу захватили всемирную известность и доверие игроков. Первая консоль под названием Xbox, вышедшая в далеком 2001 году, значительно уступала PlayStation 2 по количеству проданных приставок. Однако все поменялось с выходом Xbox 360 - консоли седьмого поколения, которая стала по-настоящему "народной" для обитателей Рф и стран СНГ - Игры для Xbox 360 прошивка LT 3.0 торрент. Сайт Ru-Xbox.Ru является популярным ресурсом в числе поклонников приставки, поскольку он предлагает игры для Xbox 360, которые поддерживают все существующие версии прошивок - совсем бесплатно! Для чего играть на оригинальном железе, в случае если есть эмуляторы? Для Xbox 360 игры выходили долгое время и представлены как посредственными проектами, так и хитами, многие из которых даже сегодня остаются уникальными для это консоли. Некие гости, желающие сыграть в игры для Xbox 360, могут задать вопрос: зачем нужны игры для прошитых Xbox 360 freeboot либо различными версиями LT, если имеется эмулятор? Рабочий эмулятор Xbox 360 хоть и существует, однако он требует производительного ПК, для покупки которого будет нужно вложить существенную сумму. К тому же, разнообразные артефакты в виде исчезающих текстур, отсутствия некоторых графических эффектов и освещения - могут значительно испортить впечатления об игре и отбить желание для ее дальнейшего прохождения. Что предлагает этот портал? Наш интернет-сайт вполне посвящен играм для приставки Xbox 360. У нас можно совершенно бесплатно и без регистрации загрузить игры на Xbox 360 через torrent для следующих версий прошивок консоли: - FreeBoot; - LT 3.0; - LT 2.0; - LT 1.9. Каждая прошивка имеет свои особенности обхода интегрированной защиты. Потому, для запуска той либо прочей игры будет нужно загрузить специальную ее версию, которая стопроцентно адаптирована под одну из четырех вышеперечисленных прошивок. На нашем интернет-сайте можно без усилий получить желаемый проект под подходящую прошивку, поскольку возле каждой игры присутствует название версии (FreeBoot, LT 3.0/2.0/1.9), под которую она адаптирована. Пользователям данного ресурса доступна особая категория игр для 360-го, предназначенных для Kinect - специального дополнения, которое считывает все движения 1-го либо нескольких игроков, и позволяет управлять с их помощью компьютерными персонажами. Большой выбор ПО Не считая способности скачать игры на Xbox 360 Freeboot либо LT разных версий, здесь можно получить программное обеспечение для консоли от Майкрософт: - всевозможные версии Dashboard, которые позволяют кастомизировать интерфейс консоли под свои нужды, сделав его более комфортным и нынешним; - браузеры; - просмотрщики файлов; - сохранения для игр; - темы для консоли; - программы, для конвертации образов и записи их на диск. Кроме вышеперечисленного игры на Xbox 360 Freeboot можно запускать не с дисковых, а с USB и других носителей, используя программу x360key, которую можно достать на нашем веб-сайте. Посетителям доступно множество нужных статей, а также форум, где можно пообщаться с единомышленниками либо попросить совета у более опытных владельцев консоли.

    ReplyDelete
  5. Большинство пользователей Инстаграма хотели бы увеличить количество фолловеров, просмотров, комментариев и лайков. Самым быстрым способом может стать накрутка за деньги, конкретно такие услуги предлагают на веб-сайте Krutiminst.ru - накрутка подписчиков инстаграм 10000 за 5 минут

    ReplyDelete
  6. Бонусы и поощрений в БК 1хбет существенно повышает привлекательность компании в глазах игроков. Весьма выгодные предложения доступны и новичкам, и гостям, уже имеющим опыт работы на платформе. В числе внушительного ассортимента бонусной программы очень очень просто потеряться. Каждый промокод 1хбет гарантирует право на определенные преференции - промокод 1xbet.

    ReplyDelete
  7. БК MelBet пользуется большой известностью на российском рынке: -Деятельность фирмы лицензирована; - Игрокам предоставлен внушительный перечень ставок - в формате live и предматчевых; - Тут нет задержек с выплатами. Линия ставок неописуемо презентабельна. Для того, чтобы получить выгодный бонус на совершение ставок, следует всего лишь использовать промокод MelBet RS777. Получить промокод вы можете на ставку либо на депозит. Каждое предложение имеет свои особенности отыгрыша - MelBet промокод на сегодня.

    ReplyDelete
  8. В 2023 году промокод 1хбет предоставляющий наибольший бонус - 1XFREE777. Бонусные денежные средства начисляются автоматом на отдельный счет и их сразу вы можете использовать для игры - промокод 1хбет при регистрации.

    ReplyDelete