Replacing EXISTS JOIN with JOIN - axapta

I am having trouble with the AX2012 class (default AX2012 class and code, no modifications have been made on it): CustVendTransDetails in the method calcCashDiscounts
The following query is giving me the error The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.:
if (TaxParameters::canApplyCashDiscOnInvoice_ES())
{
insert_recordset tmpValue
(CustVendTransRefRecId, AmountMST)
select CustVendTransRefRecId
from _custVendAccountStatementIntTmpProcessing
exists join custVendTransLoc
where
custVendTransLoc.RecId == _custVendAccountStatementIntTmpProcessing.CustVendTransRefRecId
exists join firstOnly subledgerVoucherGeneralJournalEntry
where
subledgerVoucherGeneralJournalEntry.Voucher == custVendTransLoc.Voucher &&
subledgerVoucherGeneralJournalEntry.AccountingDate == custVendTransLoc.TransDate
exists join generalJournalEntry
where
generalJournalEntry.RecId == subledgerVoucherGeneralJournalEntry.GeneralJournalEntry &&
generalJournalEntry.Ledger == Ledger::current()
join AccountingCurrencyAmount from generalJournalAccountEntry
where
generalJournalAccountEntry.GeneralJournalEntry == generalJournalEntry.RecId &&
(generalJournalAccountEntry.PostingType == LedgerPostingType::CustCashDisc ||
generalJournalAccountEntry.PostingType == LedgerPostingType::VendCashDisc);
update_recordSet _custVendAccountStatementIntTmpProcessing setting
UtilizedCashDisc = tmpValue.AmountMST,
PossibleCashDisc = tmpValue.AmountMST
join tmpValue
where
tmpValue.CustVendTransRefRecId == _custVendAccountStatementIntTmpProcessing.CustVendTransRefRecId;
}
I understand why, but I am not sure how to solve this problem. Will it be a problem to replace the exist join with a normal join?
Replacing the exist join with join, does solve my problem, but I am not sure what difference it will make to the data? Because it is only is selecting 1 field?

You can try to switch the order of joins:
insert_recordset tmpValue (CustVendTransRefRecId, AmountMST)
select CustVendTransRefRecId
from _custVendAccountStatementIntTmpProcessing
join AccountingCurrencyAmount from generalJournalAccountEntry // Moved up
where generalJournalAccountEntry.PostingType == LedgerPostingType::CustCashDisc ||
generalJournalAccountEntry.PostingType == LedgerPostingType::VendCashDisc
exists join custVendTransLoc
where
custVendTransLoc.RecId == _custVendAccountStatementIntTmpProcessing.CustVendTransRefRecId
exists join firstOnly subledgerVoucherGeneralJournalEntry
where
subledgerVoucherGeneralJournalEntry.Voucher == custVendTransLoc.Voucher &&
subledgerVoucherGeneralJournalEntry.AccountingDate == custVendTransLoc.TransDate
exists join generalJournalEntry
where
generalJournalEntry.RecId == subledgerVoucherGeneralJournalEntry.GeneralJournalEntry && &&
generalJournalEntry.RecId == generalJournalAccountEntry.GeneralJournalEntry && // Moved from join
generalJournalEntry.Ledger == Ledger::current();

Replacing the exist join with join will not fix your issue. Exist is a way to join to essentially inner join to a table without returning any fields.
The query should return CustVendTransRefRecId from _custVendAccountStatementIntTmpProcessing and AccountingCurrencyAmount from generalJournalAccountEntry which is exactly what the insert is expecting.
I expect the query isn't actually returning anything. Check the criteria it is using and check the data.

Related

How express SQL left join with IS NULL in LINQ

I'm trying to write a LINQ equivalent of the following SQL:
SELECT i.ItemID, i.ItemName
FROM Items AS i
LEFT JOIN BillOfMaterials AS bom ON bom.ItemID = i.ItemID
WHERE bom.ItemID IS NULL
This returns a set of items where the itemid does not appear in the BillofMaterials.ItemID column.
I've tried the following which (not surprisingly) doesn't work:
from i in ctx.Items
join b in ctx.BillOfMaterials on i.ItemID equals b.ItemID
into joinedTable
from j in joinedTable.DefaultIfEmpty().Where(w => w.ItemID == null)
select new
{
i.ItemID,
i.ItemName
};
Since you don't seem to be interested here in the actual BOM objects, only if they exist or not, I would recommend using an expression with just an existence check:
from i in ctx.Items
where !ctx.BillOfMaterials.Any(b => i.ItemID == b.ItemID)
select new
{
i.ItemID,
i.ItemName
};
It is shorter, and it describes better what you are checking for. Also depending on how smart EF is (or not), it could even be more efficient because the BillOfMaterials data will never be loaded into .NET memory as BillOfMaterial objects.
WHERE bom.ItemID IS NULL in sql corresponds to j == null in Linq.
Where(w => w.ItemID == null) will not work here, because the w was null and an exception was thrown.
The right syntax for the left join Linq is like :
from i in ctx.Items
join b in ctx.BillOfMaterials on i.ItemID equals b.ItemID into joinedTable
from j in joinedTable.DefaultIfEmpty()
where j == null
select new
{
i.ItemID,
i.ItemName
};
I hope you find this helpful.

while select with join firstOnly

Is there any chance i could join two tables like this ?
while select SalesId from salesTable
//group by SalesId
where salesTable.SalesId == "xxx006932683"
join firstOnly SalesPrice, ItemId, LineNum from salesLine
//group by SalesId
order by salesLine.LineDisc asc, salesLine.SalesPrice desc
where salesLine.SalesId == salesTable.SalesId
{
info(strFmt("Sales id : %1 line %2 item %3 price %4", salesLine.SalesId, salesLine.LineNum, salesLine.ItemId, salesLine.SalesPrice));
}
So, for each line in SalesTable, join it with the only one line in SalesLine with the same SalesId and satisfying the order condition.
To be honest, i have tried a lot of groupings and orderings and maxOfs, minOfs with no success... so here i am asking for an idea.
You can not do that in one select statement.
First you can create a view on sales line with grouping on SalesId and maxOf, minOf, ... on fields you need.
This view should return only one record for each SalesId. Than you can join this view to sales table.
If you only want to get first line of order then you have to do nested selects.
Best way is to create a temp table with fields you need and fill it with data.
while select SalesId from salesTable
{
select firstOnly SalesPrice, ItemId, LineNum from salesLine
order by salesLine.LineDisc asc, salesLine.SalesPrice desc
where salesLine.SalesId == salesTable.SalesId
;
//insert into temp table
info(strFmt("Sales id : %1 line %2 item %3 price %4", salesLine.SalesId, salesLine.LineNum, salesLine.ItemId, salesLine.SalesPrice));
}
But in your case (because you have where statement on SalesId <- unique) this will work fine
select firstOnly SalesPrice, ItemId, LineNum from salesLine
order by salesLine.LineDisc asc, salesLine.SalesPrice desc
where salesLine.SalesId == "xxx006932683";
I have to admit this is the most strangest query I have written so far :
SalesQuotationTable salesQuotationTable;
SalesQuotationLine salesQuotationLine;
CustQuotationSalesLink custQuotationSalesLink;
CustQuotationJour custQuotationJour;
;
while select maxof(QuotationId), maxof(CurrencyCode) from salesQuotationTable
group by QuotationId, CurrencyCode, RecId
where salesQuotationTable.QuotationStatus == SalesQuotationStatus::Sent
&& salesQuotationTable.QuotationType == QuotationType::Sales
//&& salesQuotationTable.QuotationId == '00015683_042' just for testing
join maxof(lineNum), minof(lineAmount), maxof(QuotationId) from salesQuotationLine
group by lineNum, lineAmount, QuotationId, RecId
where salesQuotationLine.QuotationId == salesQuotationTable.QuotationId
&& salesQuotationLine.QuotationStatus == SalesQuotationStatus::Sent
&& salesQuotationLine.QuotationType == QuotationType::Sales
&& salesQuotationLine.SalesQty > 0
//duplicate values were coming from here, grouping was the way to go
join maxof(QuotationDate), maxof(QuotationId) from custQuotationSalesLink
group by OrigQuotationId
where custQuotationSalesLink.OrigQuotationId == salesQuotationTable.QuotationId
join maxof(QuotationDate) from custQuotationJour
order by custQuotationJour.QuotationId
where custQuotationJour.QuotationId == custQuotationSalesLink.QuotationId
&& custQuotationJour.QuotationDate == custQuotationSalesLink.QuotationDate
A few notes:
1. Instead of
select firstonly custQuotationSalesLink
order by QuotationDate desc, QuotationId desc
where custQuotationSalesLink.OrigQuotationId == this.QuotationId
I have used
join maxof(QuotationDate), maxof(QuotationId) from custQuotationSalesLink
group by OrigQuotationId
where custQuotationSalesLink.OrigQuotationId == salesQuotationTable.QuotationId
The party starts here, from what i have seen, once using a group by, all the fields from the other tables seem to be empty. So the solution is to add group by everywhere.
You see i am adding the RecId into groupings to be sure i am not really grouping anything :)
In order to get fields with values you have to add an aggregate function in the select clause. Ok , cool, why not, as long as i am not grouping for real.
2. But the catch for me was at the last part:
join maxof(QuotationDate) from custQuotationJour
order by custQuotationJour.QuotationId
where custQuotationJour.QuotationId == custQuotationSalesLink.QuotationId
&& custQuotationJour.QuotationDate == custQuotationSalesLink.QuotationDate
That order by did the trick. I don t know why. If i switch it with a group by which seems normal to me, i get duplicated values. So all the groupings added before are just losing their relevance. I guess sometimes a bit of luck has to join the game too. I mean, why thinking at order by there. Maybe because it s Wednesday, i don t know.
I had to use some aggregation on the last join because otherwise i wouldn t have got a value for the QuotationDate field which in fact is the whole goal of this work.
This link helped me a lot :
http://axatluegisdorf.blogspot.ca/2010/07/select-group-by-and-join-order-by.html

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.

Red hand on a lot of transactions

We suddenly have a lot of red hands in "Open vendor transactions". We had weird problems due to a lack of database maintenance plan, now the system is up and running, but too many red hands are showing.
I saw this on MS: http://support.microsoft.com/kb/894412
But this is not very practical as it's not one offending record, but many.
Is there any way to spot marked transactions that should not be, quickly, in transact SQL ?
Is finding orphans in SpecTrans VS VendTransOpen a good start ?
Thanks!
EDIT: Thinking of it, finding orphaned records in SpecTrans probably won't help as a red hand probably means the transaction was found in SpecTrans...
EDIT#2: I found this to unmark certain transactions, but when I do what is being proposed, I cannot find any associated journal. This might be the issue.
After backing up the table, this is what I ended up doing:
delete from SPECTRANS where SPECTRANS.RECID in
(
select st.RECID from VENDTRANS vt
inner join VENDTRANSOPEN vto on vto.REFRECID = vt.RECID
inner join SPECTRANS st on st.REFRECID = vto.RECID
where st.SPECTABLEID=470 and st.DATAAREAID in ('company1', 'company2')
)
A cause for "red hands" can sometimes be SpecTrans records that point to LedgerJournalTrans records which no longer exists.
A job for deleting them (current company only):
static void RedHandDirtyVendRemove(Args _args)
{
SpecTrans st;
VendTrans vt;
VendTransOpen vto;
LedgerJournalTrans ljt;
while select count(RecId) from st
group SpecTableId
where st.RefTableId == tableNum(VendTransOpen) &&
st.SpecCompany == curext() &&
st.RefCompany == curext()
exists join vto
where vto.RecId == st.RefRecId
{
info(strFmt('%1: %2', st.RecId, tableId2name(st.SpecTableId)));
}
while select st
where st.RefTableId == tableNum(VendTransOpen) &&
st.SpecTableId == tableNum(LedgerJournalTrans) &&
st.SpecCompany == curext() &&
st.RefCompany == curext()
join vto
where vto.RecId == st.RefRecId
join vt
where vt.RecId == vto.RefRecId
notExists join ljt
where ljt.RecId == st.SpecRecId
{
info(strFmt('%1: %2 %3', st.RecId, vt.Voucher, vt.AccountNum));
}
delete_from st
where st.RefTableId == tableNum(VendTransOpen) &&
st.SpecTableId == tableNum(LedgerJournalTrans) &&
st.SpecCompany == curext() &&
st.RefCompany == curext()
exists join vto
where vto.RecId == st.RefRecId
exists join vt
where vt.RecId == vto.RefRecId
notExists join ljt
where ljt.RecId == st.SpecRecId;
info(strFmt('%1', st.RowCount()));
}

LINQ - EF join difficulty

I have two tables:
Phase :
long ID
string Name
and another Activity :
long ID
string Name
long PhaseID
I already know the name of the phases and I want to get the activity for those particular phases. Do i add PhaseName to the activity table or do I do it through join in LINQ?
Maybe something like this?
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
... and here im not sure how to finish this query..
Thanks for your help!
The code that you've provided will use an Inner Join to find all Activities where the Phase with Name "Preplanning" exists.
To finish your query you need to add a select clause.
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
select a.Name
will return IEnumerable<string> of all activity names.
Just select activity, as you want:
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
select a;
Here is how query expression should look like:
A query expression must begin with a from clause and must end with a select or group clause. Between the first from clause and the last select or group clause, it can contain one or more of these optional clauses: where, orderby, join, let and even additional from clauses. You can also use the into keyword to enable the result of a join or group clause to serve as the source for additional query clauses in the same query expression.
Same as puzzling image:
With method syntax you don't need to end query with something special:
var query = entities.Phases
.Where(p => p.Name == "Preplanning")
.Join(entities.Activities, p => p.Id, a => a.PhaseId, (p,a) => a);
No need to do a join if you only need data from one of the tables. You can apply a filter instead:
var q = entities.Activities.Where(a =>
entities.Phases.Any(p => a.PhaseId == p.Id && p.Name == "Preplanning"));

Resources