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.
Related
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;
}
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
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.
I have a view that contain all data related to employee.
it had about 350k records.
I have to make a name search functionality.
That will retrieve all the data that matches the keyword entered.
The query performance is very slow it takes 15-20 seconds to retrieve data.
Cost-15000
My query:
SELECT H.PERSON_ID,
B.EMPLOYEE_ID,
INITCAP(B.FIRST_NAME) EMP_FNAME,
INITCAP(B.MIDDLE_NAME) EMP_MNAME,
INITCAP(B.LAST_NAME) EMP_LNAME,
B.EMPLOYEE_TYPE PERSON_DESC,
B.EMPLOYMENT_STATUS STATUS_TYPE,
EA.BASE_BRANCH
FROM EMPLOYEE_BASIC_DTLS B,
EMP_ASSIGNMENT_DTLS_MV EA,
EMPLOYEE_HIS_DEPNDENT_TBL H
WHERE B.PERSON_ID = EA.PERSON_ID
AND B.PERSON_ID = H.PERSON_ID
AND ((UPPER(B.FIRST_NAME) LIKE
('%' || V_SEARCH_PARAM1 || '%')) OR
(UPPER(B.MIDDLE_NAME) LIKE
('%' || V_SEARCH_PARAM1 || '%')) OR
(UPPER(B.LAST_NAME) LIKE
('%' || V_SEARCH_PARAM1 || '%')))
AND TRUNC(SYSDATE) BETWEEN EA.EFFECTIVE_START_DATE AND
EA.EFFECTIVE_END_DATE
AND UPPER(H.RELATIONSHIP_CODE) = 'A';
Since EMPLOYEE_BASIC_DTLS is a view I cant use indexing.
While it's true you can't put an index on a view, you can certainly put indexes on the underlying tables. However, as noted by #JustinCave even if you do add indexes to the appropriate tables this query still won't use them because of the use of LIKE. Additionally, because the UPPER function is being applied to the FIRST_NAME, MIDDLE_NAME, and LAST_NAME columns you'd need to define your indexes as function-based indexes. For example, if the 'real' table accessed by the EMPLOYEE_BASIC_TABLE view is called EMPLOYEES you could define a function-based index on the FIRST_NAME column as
CREATE INDEX EMPLOYEES_UPPER_FIRST_NAME ON EMPLOYEES (UPPER(FIRST_NAME));
I suggest you consider whether the LIKE comparisons are really needed, as working around those to get better performance is going to be difficult.
If you'd like to investigate Oracle Text indexes you can find the documentation here. I think you'll find it's more suited to document or document fragment indexes, but perhaps it would give you some ideas.
Share and enjoy.
As one may look for any name or any part of a name there is no way to create an index containing the values to be searched beforehand. So that won't help you here. Oracle will do a full table scan to check every single string for a match.
What you can do though is to speed up that scan.
You can speed up a full table scan by parallelizing it via /*+parallel(EMPLOYEE_BASIC_TABLE,4)*/ for instance. (This would be my advice here.)
Or you can avoid a full table scan by having one index per column, well knowing that there are many repeatedly used names, so that every name is scanned just once. Then you would use function based keys on the underlying table as Bob Jarvis suggests, because you are using the upper function on any name. Fastest would be a combined index:
create bitmap index idx_name_search on EMPLOYEE_BASIC_TABLE (upper(first_name || '|' || middle_name || '|' || last_name))
so there is just one index to look up. (You would have to use exactly this expression in your query of course: WHERE upper(first_name || '|' || middle_name || '|' || last_name) like '%JOHN%'.) But still, you don't know what will be searched for in advance, and as '%JOHN%' may effect only 2% of your table data, '%E%' may affect 80%. The optimizer would never know. You could at least guess and have to different select statements, one with a full table hint you'd use when the search string contains at least three letters and one with an index hint you'd use otherwise, for instance.
You see, that gets quite complicated the more you think about it. I suggest to try the parallel hint first. Maybe this already speeds things up sufficiently.
I am trying to perform queries using the OR operator as following:
MapReduceResult result = riakClient.
mapReduce("some_bucket", "Name:c1 OR c2").
addMapPhase(new NamedJSFunction("Riak.mapValuesJson"), true).
execute();
I only get the 1st object in the query (where name='c1').
If I change the order of the query (i.e. Name:c2 OR c1) again I get only the first object in query (where name='c2').
is the OR operator (and other query operators) supported in the java client?
I got this answer from Basho engeneer, Sean C.:
You either need to group the terms or qualify both of them. Without a field identifier, the search query assumes that the default field is being searched. You can determine how the query will be interpreted by using the 'search-cmd explain' command. Here's two alternate ways to express your query:
Name:c1 OR Name:c2
Name:(c1 OR c2)
both options worked for me!