How to add a complex range expression to an AOT query in the AOT - dynamics-ax-2012

I am working on a report, and for my data set i need to use an or statement. in SQL my query would look like:
SELECT recId, etc... FROM CustTrans
WHERE (CustTrans.Closed IS NULL AND CustTrans.Invoice IS NULL)
OR (CustTrans.Invoice IS NOT NULL)
I would translate this then into range like the following (stuck on the RecId field)
Value:
((CustTrans.Invoice == ‘’) && (CustTrans.Closed == ‘’) || (CustTrans.Invoice != ‘’))
I have found numerous places explaining that this is the proper syntax, although all of them are using the programmatic method of creating a query. I need to keep mine in the AOT so the customer can modify it later on if they want, and every combination of quotes, parens, and order still does not work. Sometimes AX will accept my range value string, but it doesn't do anything, and sometimes it will give me a random syntax error.
Is there a way to accomplish what I'm looking to do? have I missed some syntax, or does AX really not let you use OR on a query without a union and another view?
Thanks!

You can create a Query in the AOT and then over-ride the init method for the Query. Where you can put these complex SQL statements and get your work done.

Related

How to Join a data source using a `Like` `LEFT`

I first created a SQL query and now I'm converting it into an AOT query in AX 2012 R3. I have everything else working except for one thing. I have a join in my SQL that is: JOIN dbo.INVENTJOURNALTABLE AS INV3 ON INV.INVENTBATCHID LIKE left(INV3.DESCRIPTION,17). In SQL this works and returns the data that I need.
I don't know how to create this join in the AOT query though.
That INV3.DESCRIPTION is a string that contains the InventBatchID.
Is there a way to do this kind of join?
In order to accomplish what you want with a Query object, it's difficult/unusual, and AX isn't designed to do this in a straight forward way and requires a very creative solution.
I would generally push towards writing a pure x++ query as I don't believe LEFT and LIKE can be natively combined, especially in a query. You can use * in the value for "like" as an option.
You may be able to accomplish using expressions in query ranges somehow.
If you must have a query, a way I can think is by combining a View, Computed Column, and a Query...and I can't guarantee it will work, but I can give you enough to have something to test with. Some of the information in this answer is pretty concentrated so look closely at everything to understand it.
Create a View, add the below computed column, then add it to a Query as pictured below. For the computed column, you need to add a new String field to the view and set the ViewMethod property to the name of the method. See here for more info about adding Computed Columns.
The Computed Column accomplishes the left(..., 17) part and you can browse the view to confirm.
The last part is trying to join either by a relation (pictured below, but it does not accomplish the like) or setting the Value property using an expression by following the link above. You may need to create a custom expression in \Classes\SysQueryRangeUtil. You have some experimenting to do to see if it works.
private static server str compColDescLeft()
{
#define.ViewName(InventJournalTableView)
#define.DataSourceName("InventJournalTable_1")
#define.FieldDescription("Description")
#define.LeftCount("17")
str sReturn;
str sLeftDesc;
DictView dictView;
dictView = new DictView(tableNum(#ViewName));
sLeftDesc = dictView.computedColumnString(#DataSourceName, #FieldDescription, FieldNameGenerationMode::FieldList, true);
sReturn = "left(" + sLeftDesc + ", " + #LeftCount + ")";
return sReturn;
}

AX QueryService 2012 "OR" query - how to?

can anyone give me a nudge in the right direction as to how to do an "OR" query via the Query Service?
In order to do an "AND" query I can simply add two (or more) "Ranges" - this sort of jazz:
// build and add our new filter
QueryDataRangeMetadata range = new QueryDataRangeMetadata
{
TableName = dataSource.Table,
FieldName = fieldName,
Value = fieldValue,
Enabled = true
};
dataSource.Ranges[ranges.Length - 1] = range;
..but how does one do an "OR" ??
OR in AX queries can be made by any of these tree methods:
Adding another query range on the same field
Have a range with comma-separated values
Use a query expression
See this question if the OR involves different tables.
These options should apply Query Service as well.
...thanks for the suggestions, but in the end I don't think it's helped me much :-(
1.Adding another query range on the same field
=> unfortunately I need to query on a different field ..essentially "WHERE A=1 or B=2" type thing..
Have a range with comma-separated values
=> unfortunately also doesn't work with separate fields
Use a query expression
=> unfortunately I could not get this to work, and am coming round to the idea that it is not actually a feature the "AX Query Service 2012" (as per title) that is the tech I am working with (looks like it is something within AX??). Note that when defining a Range the "FieldName" property of the QueryDataRangeMetadata instance is required (and no - comma separated field names did not work).
On the plus side I have now got it working :-) ...the way I did it was to add the table twice as two different datasources to the query- seems nasty, but what's a man to do...
thanks again-
Oli.
I have used this in the past:
queryBuildRange.value(strFmt('((%1 == %2) || ((%1 == %3) && (%4 == "%5")))',
fieldStr(InventTable, ItemType),
any2int(ItemType::Service),
any2int(ItemType::Item),
fieldStr(InventTable, ProjCategoryId),
queryValue("Spares")));
Probably is not the cleanest way to do it but will do the job.

Add a condition to a form datasource with OR on two columns

I have to build a specialized form which queries the InventTrans table for some purchase transactions.
The transactions must be selected, either with a non-empty Voucher, either with a non-empty Packing slip Id (PackingSlipId).
While looking how to specify this in a query range, I found that it was possible to associate this query range to the RecId of the table. Is this the usual and classic way to do this ? See my example (which seem to work).
QueryBuildDataSource qbds;
;
qbds = this.query().dataSourceTable(tableNum(inventTrans));
sysQuery::findOrCreateRange(qbds, fieldNum(InventTrans, RecId)).value(
strfmt('(%1 != "") || (%2 != "")',
fieldstr(InventTrans, Voucher),
fieldstr(InventTrans, PackingSlipId)));
Those are called range expressions and is the only way to state more complex queries with a Query object - see this MSDN page
In addition have a look at this Axaptapedia page (thanks to FH-Inway)
Regarding if it is the classical way: No - only use it if necessary

what difference between ax query and select

I'm looking for difference between ax query and select (or while select)
In this example i don't see what's i can not do with statement select : the example of ax query in msdn
I think I misunderstood the syntax of ax query ranges :'(
A (while) select is a 'one use' statement, ie, you put it inline in your code and it is used only there.
A query can be setup to require parameters and can be used multiple times throughout your class or saved into the AOT for use in any class.
Generally I only use select statements for simple queries where its not worth the effort to create a query, for anything more complex I use queries.

Basic SQL count with LINQ

I have a trivial issue that I can't resolve. Currently our app uses Linq to retrieve data and get a basic integer value of the row count. I can't form a query that gives back a count without a 'select i'. I don't need the select, just the count(*) response. How do I do this? Below is a sample:
return (from io in db._Owners
where io.Id == Id && io.userId == userId
join i in db._Instances on io.Id equals i.Id **select i**).Count()
;
The select i is fine - it's not actually going to be fetching any data back to the client, because the Count() call will be translated into a Count(something) call at the SQL side.
When in doubt, look at the SQL that's being generated for your query, e.g. with the DataContext.Log property.
Using the LINQ query syntax requires a select statement. There's no way around that.
That being said, the statement will get transformed into a COUNT()-based query; the select i is there only to satisfy the expression system that underlies the LINQ query providers (otherwise the type of the expression would be unknown).
Including the select will not affect the performance here because the final query will get translated into SQL. At this point it will be optimized and will be like select (*) from ......

Resources