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;
}
Related
I already used the search here (and other forums as well) but haven't found an answer exacty to what I'm trying to do.
I know that it can easily be done in some other way, and this is just a small sandbox-framework I'm coding for a University course... in a real environment I'd just take Spring, Hibernate etc.
So what I did was coding myself a small generic Data Access Layer with POJOs, working with generic methods to retrieve, check or insert data to the database (Oracle). Most of this is done through PreparedStatements.
This is working as long as I don't have joins... is it possible to put in a Column as parameter?
Example:
Table A has Attribute X + others
Table B has Attribute Y + others
PreparedStatement with query SELECT * FROM A,B WHERE "A"."X" = ?
And then fill in "B"."Y" as the parameter...
The database doesn't throw me an error or exception, but the ResultSet returned after executing the statement is empty. Is it just not possible to do, or am I just missing some escaping?
I'm using PreparedStatement.setString(int index, String value) to fill in the parameter... in lack of ideas which other setX method I could use...
Again, in a real project I'd never code that myself, but rather use something like Spring or Hibernate and not re-invent the wheel, but I see it as an interesting exercise to code such a generic small data access layer myself.
No, JDBC does not allow this. Only column values can be set. If you want to make dynamic changes to the sql statement you will have to do it before you create the PreparedStatement.
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.
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 been using Linq to SQL for a while now on one of my sites and over time the code I am using to query the database has gotten a little messy so I decided to re-write, originally my queries were all handled exclusively by Linq but recently there has been a demand for more advanced search features which has led me more towards using ExecuteQuery and handwriting my SQL statements the problem is that I cannot for the life of me get the Join statement to work properly.
I have two tables in my databases, t_events and t_clients. The only thing similar between the two tables is that they both have a clientid field (the id of the client the event is for). What I need to be able to do is pull all of the events into the page (which works fine) but I dont want to show the clientid on the page I need to show the client name. Originally I had a join clause that handled this nicely:
var eve = from p in db.t_events
join c in db.Clients on p.clientid equals c.clientid
where p.datedue <= thisDay && p.status != "complete"
select new { p.eventname, p.datedue, p.details, p.eventid, p.status, c.clientname };
With the redesign of the page however I am having issues recreating what linq has done here with the join. My current code:
StringBuilder sqlQuery = new StringBuilder("SELECT * FROM dbo.t_events JOIN dbo.t_clients ON dbo.t_events.clientid=dbo.t_clients.clientid");
var query = db.ExecuteQuery<t_events>(sqlQuery.ToString());
foreach (var c in query)
{
counter = counter + 1;
MyStringBuilder.Append("<tr class='"+c.status+"'><td><a href='searchdetails.aspx?id="+c.eventid+"'>"+c.eventname+"</a></td><td>" +c.clientname+ "</td></tr>");
}
in the foreach loop I have you can see I am trying to pull in c.clientname (which doesnt work) as it is not on the t_events database, changing this to c.clientid makes the code work, I am not sure what the issue is as taking that same SQL and running the query directly off the sql management tool works like a charm. Any ideas on this issue would be greatly appreciated!
FIXED!
DaveMarkle suggested using a view, which was by far a much easier way of doing this. I created a view that joins the two tables together with the fields I need and run my queries against it, simple and effective, I thank you!
Erm - so maybe we should have an answer here then so the question drops off the 'unanswered' list.
As Dave Markle stated.
Use a view.
Another option!
Execute the query twice: once with db.ExecuteQuery<t_events> and once db.ExecuteQuery<t_clients>. Now that you have both events and clients you can re-join them client-side by matching client_id.
Developing a website and just trying to get back into the swing of (clever) SQL queries etc, my mind had totally gone tonight!
There is a website http://www.ufindus.com/ which has a textbox allowing you to enter either a place name or a postcode/zipcode. I am trying to do something similiar but I am rubbish at SQL - so how do you construct an SQL statement that could potentially look at 2 columns (i.e. place and postcode) because you can't query both fields for the same value e.g
place = 'YORK' AND postcode = 'YORK'
or
place = 'YO21 5EA' AND postcode = 'YO21 5EA'
so do you have to put some logic in to be intelligent enough to detect whether it looks like a place name or a postcode - that just seems too complicated to me!! Any help would be much appreciated.
You could use an "OR" to get the job done. For example,
place = 'YORK' or postcode = 'YORK'
You might also do better using the LIKE statement, as in
WHERE place LIKE 'YORK%' or postcode LIKE 'YORK%'
(this assumes both place and postcode are character-based columns)
why not use OR instead of AND?
place = #textboxvalue OR post = #textboxvalue
What's wrong with attempting to match on the place and postcode? If I put in 'York' and (somewhere) that happens to be a valid postcode, I should get that result. As for preventing the same thing being entered twice, well, you can handle that on the validation prior to doing the database call.
Ah. Guess I was a bit slow on the up-take. Yes... what the others suggested is right, 'OR' is what you were looking for. I misinterpreted.
Ok, first I'm assuming that you have a table with a mapping of postcodes to placenames.
Let's call this table 'postcode' with columns 'postcode' and 'postplace'. Both of these are of a char-type.
Then.. whatever you do, make sure the input from the user is not part of dynamic sql. Make sure it is a parameter. Otherwise, you are inviting SQL injection attacks that can really ruin your day. This is important.
Our user input is in #textboxstring.
Given this, you can get the postcode and postplace like this:
select #textboxstring = RTRIM(#textboxstring) + '%';
select postcode, postplace
from postcode
where postcode like #textboxstring or postplace like #textboxstring;
Note that I'm modifying #textboxstring to get wildcard match with like without having to use dynamic sql.
If the postcode was integer, you would need to convert the input to int before executing the sql. So with a #textboxint as well, you could do this:
select #textboxstring = RTRIM(#textboxstring) + '%';
select postcode, postplace
from postcode
where postcode = #textboxint or postplace like #textboxstring;
Oh, and you need to handle that your search can have multiple results. You probably only want the first row.