Trying to join firstonly using AX query object - axapta

My request is that a client wants to put in a date range (typically a month) and pull all general ledger journals that have at least one line posted in that date range. They will post journals in March for January's period for example, and want to know which journals have that data.
The basic idea is LedgerJournalTable to a firstonly LedgerJournalTrans. I'm not the best with the query object. Why isn't my query working? It's returning multiple journals instead of just one. I was thinking I could group by and be OK, but I'd think this would work.
static void Job38(Args _args)
{
Query q;
QueryRun queryRun;
QueryBuildDatasource qbd;
QueryBuildDatasource qbd2;
QueryBuildRange qbr;
LedgerJournalTable ledgerJournalTable;
;
info(strfmt("%1", date2strxpp(str2date('10/01/2011', 213))));
q = new Query();
qbd = q.addDataSource(tablenum(LedgerJournalTable));
qbd2 = qbd.addDataSource(tableNum(LedgerJournalTrans));
qbd2.relations(true);
qbd2.firstOnly(true);
qbd2.joinMode(JoinMode::InnerJoin);
qbr = qbd2.addRange(fieldNum(LedgerJournalTrans, TransDate));
qbr.value(strfmt('(TransDate > %1) && (TransDate < %2)', Date2StrXpp(str2date('10/01/2011', 213)), Date2StrXpp(str2date('10/31/2011', 213))));
queryRun = new QueryRun(q);
while (queryRun.next())
{
ledgerJournalTable = queryRun.get(tableNum(LedgerJournalTable));
info(strfmt("%1", ledgerJournalTable.JournalNum));
}
}

Have you tried JoinMode::ExistsJoin?

Related

How do i get the Count of InventSerialId from InventDim

How do i create a query or using select to get the count of InventSerialId base on a given Itemid, InventLocationId and where the inventSum.PhysicalInvent > 0 or inventSum.Picked > 0.
This is not directly possible using X++.
Consider:
static void _TestDim(Args _args)
{
ItemId itemId = '123';
InventSum inventSum;
InventDim inventDim;
Query q = new Query();
QueryBuildDataSource ds = q.addDataSource(tableNum(InventSum), 's');
QueryRun qr;
;
// ds.addRange(fieldNum(InventSum,ItemId)).value(queryValue(itemId));
ds.addRange(fieldNum(InventSum,Closed)).value(queryValue(NoYes::No));
ds.addGroupByField(fieldNum(InventSum,ItemId));
ds.addSelectionField(fieldNum(InventSum,PhysicalInvent),SelectionField::Sum);
ds.addSelectionField(fieldNum(InventSum,Picked),SelectionField::Sum);
q.addHavingFilter(ds, fieldStr(InventSum,PhysicalInvent), AggregateFunction::Sum).value('>0');
// q.addHavingFilter(ds, fieldStr(InventSum,Picked), AggregateFunction::Sum).value('((s.Picked >0)||(s.PhysicalInvent>0))'); // This is not allowed
ds = ds.addDataSource(tableNum(InventDim), 'd');
ds.joinMode(JoinMode::InnerJoin);
ds.relations(true);
ds.addGroupByField(fieldNum(InventDim,InventSerialId));
ds.addRange(fieldNum(InventDim,InventSerialId)).value('>""');
info(q.dataSourceNo(1).toString());
qr = new QueryRun(q);
while (qr.next())
{
inventSum = qr.getNo(1);
inventDim = qr.getNo(2);
info(strFmt('%1 %2: %3 %4', inventSum.ItemId, inventDim.InventSerialId, inventSum.PhysicalInvent, inventSum.Picked));
break;
}
}
Here you aggreate PhysicalInvent and picked, and you can apply a having-filter using the query method addHavingFilter.
However, you cannot have that combined with another having-filter using a SQL or-statement.
If you try with a query expression, you will get a run-time error.
What you can do is create two views with each filter, then combine them using a union view. This is tricky but doable.
The first should select positive PhysicalInvent and the second should select PhysicalInvent == 0 and positive Picked.

AOT Query relation OR case

For example I have the following x++ query.
Select EcoResproduct
join tableX
where EcoResproduct.RecId == tableX.Product
|| EcoResproduct.RecId == tableX.DistinctProductVariant;
Is that possible to do the same thing through AOT query without using a union query or adding two times the same datasource and without using QueryBuildDataSource object and X++ at all .
Thanks in advance
PS: I made my question more clear.
Initial incorrect answer:
Is that possible to do the same thing through an AOT query without using a union query or adding two times the same datasource
No.
Correct answer, thanks to the commenters:
Query q = new Query();
QueryBuildDataSource qbds1 = q.addDataSource(tableNum(EcoResproduct));
QueryBuildDataSource qbds2 = qbds1.addDataSource(tableNum(TableX));
qbds2.addrange(fieldNum(TableX, RecId)).value(strFmt('((%2.Product == %1.RecId) || (%2.DistinctProductVariant == %1.RecId))', qbds1.name(), qbds2.name()));
info(qbds1.toString());

How to get the tables from AOT query in ax 2012

I have drop down in one page, I am selecting AOT query in first page then i will click on next button, then it has to show tables related to that query
If you have a query name, you can loop through all of its datasources like this:
str queryName = "ActivityListOpen";
int i, dbcount;
QueryBuildDataSource qbds;
Query query = new Query(queryName);
dbcount = query.dataSourceCount();
for (i = 1; i <= dbcount; i++)
{
qbds = query.dataSourceNo(i);
info(qbds.name());
}
You can also use table() method on QueryBuildDataSource to retrieve table Id.

Outer joins with where condition x++

I am trying to write a query that retrieves an item based on ItemId or item barcode.
This is using x++, Dynamics AX 2012 R2
select firstOnly * from inventTable
where (inventTable.ItemId == _upc || inventItemBarcode.itemBarCode == _upc)
outer join inventItemBarcode
where inventItemBarcode.itemId == inventTable.ItemId;
When this is translated into sql it comes out as...
FROM INVENTTABLE T1 LEFT
OUTER
JOIN INVENTITEMBARCODE T2 ON (((T2.PARTITION=?)
AND (T2.DATAAREAID=?))
AND (T1.ITEMID=T2.ITEMID))
WHERE (((T1.PARTITION=?)
AND (T1.DATAAREAID=?))
AND (T1.ITEMID=?))
You can see that is is totally omitting the OR condition in the last line.
I tried writing the query like this
select firstOnly * from inventTable
outer join inventItemBarcode
where
//join
inventItemBarcode.itemId == inventTable.ItemId
//ilc
&& (inventTable.ItemId == _upc
|| inventItemBarcode.itemBarCode == _upc);
But it puts the OR condition in the outer join ON and then returns me the first row in the InventTable.
Does anyone know how to make this work in X++?
If using AX 2012 you will have to use Query and QueryRun instead, then add your or-expression as a query expression using addQueryFilter.
static void Job117(Args _args)
{
str search = "5705050765989";
QueryRun qr = new QueryRun(new Query());
QueryBuildDataSource ds1 = qr.query().addDataSource(tableNum(InventTable));
QueryBuildDataSource ds2 = ds1.addDataSource(tableNum(InventItemBarcode));
str qstr1 = '((%1.%2 == "%5") || (%3.%4 == "%5"))';
str qstr2 = strFmt(qstr1, ds1.name(), fieldStr(InventTable,ItemId),
ds2.name(), fieldStr(InventItemBarcode,ItemBarCode),
queryValue(search));
qr.query().addQueryFilter(ds1, fieldStr(InventTable,ItemId)).value(qstr2);
ds2.joinMode(JoinMode::OuterJoin);
ds2.relations(true);
info(qstr2);
info(ds1.toString());
while (qr.next())
info(strFmt("%1", qr.getNo(1).RecId));
}
In prior AX versions you can make a view, then query the view using a query expressions by using the addRange method.

Update 2000 records with one query

I have a Database table:
Item
ID (uniqueidentifier)
Index (int)
I have a list of 2000 key-value pairs items where the key is ID and value is Index, which i need to update it. How can i update all the 2000 items from database using one single sql query?
Right now i have something like this:
// this dictionary has 2000 values
Dictionary<Guid, int> values = new Dictionary<Guid,int>();
foreach(KeyValuePair<Guid, int> item in values)
{
_db.Database.ExecuteSqlCommand("UPDATE [Item] SET [Index] = #p0 WHERE [Id] = #p1", item.Value, item.Key);
}
However, i am making too many requests to the SQL Server, and i want to improve this.
Use table value parameters to send those values to SQL Server and update Items table in one shot:
CREATE TYPE KeyValueType AS TABLE
(
[Key] GUID,
[Value] INT
);
CREATE PROCEDURE dbo.usp_UpdateItems
#pairs KeyValueType READONLY
AS
BEGIN
UPDATE I
SET [Index] = P.Value
FROM
[Item] I
INNER JOIN #pairs P ON P.Id = I.Id
END;
GO
If you really need to update in that manner and have no other alternative - the main way around it could be this rather "ugly" technique (and therefore rarely used, but still works pretty well);
Make all 2000 statements in one string, and execute that one string. That makes one call to the database with the 2000 updates in it.
So basically something like this (code not made to actually run, it's an example so t
Dictionary<Guid, int> values = new Dictionary<Guid, int>();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (KeyValuePair<Guid, int> item in values)
{
sb.Append(String.Format("UPDATE [Item] SET [Index] = {0} WHERE [Id] = '{1}';", item.Value, item.Key));
}
_db.Database.ExecuteSqlCommand(sb.ToString);

Resources