LINQ select row with the minimum date value - linq

i have this linq query:
var query = from mpItem in MPay
where mpItem.EndDate > System.DateTime.Now.Date
group mpItem by mpItem.IdGroup into mpItemGrouped
let minEndDate = mpItemGrouped.Min(p => p.EndDate)
select new
{
Id = mpItemGrouped.Key,
EndDate = mpItemGrouped.Min(p => p.EndDate),
Name = mpItemGrouped.Min(p => p.IdGroupModel.GroupName),
Price = mpItemGrouped.Min(p => p.PaySumIndividual)
};
this query should select for each IdGroup the row with the minimum EndDate
this query is selecting the minimum EndDate with another row values.

Order group by EndDate in ascending order and select first item from group - that will be item with min date. Then use this item in select statement
var query = from MPayItem in MPay
where mpItem.EndDate > System.DateTime.Now.Date
group mpItem by mpItem.IdGroup into mpItemGrouped
let minItem = mpItemGrouped.OrderBy(p => p.EndDate).First()
select new
{
Id = mpItemGrouped.Key,
EndDate = minItem.EndDate,
Name = minItem.IdGroupModel.GroupName,
Price = minItem.PaySumIndividual
};

Related

LINQ select row with max value after group by

I have following sql statement:
Select
tsl.Transaction_Id,
tsl.State_Id,
MAX(tsl."Timestamp")
from TransactionStatesLog tsl
group by tsl.Transaction_Id
How this statement can be translated to LINQ? I just want to select the whole row, where Timestamp is maximum of the group.
With this code i am able just to select TransactionId and max Timestamp from the group.
var states = (from logs in _context.TransactionStatesLog
group logs by new { logs.TransactionId } into g
select new
{
TransactionId = g.Key,
Timestamp = g.Max(x => x.Timestamp)
}).ToList();
I am working with ef core 3.1
Assuming you forgot to add tsl.State_Id to your SQL as grouping key as follows(otherwise that SQL does not work either):
Select
tsl.Transaction_Id,
tsl.State_Id,
MAX(tsl."Timestamp")
from TransactionStatesLog tsl
group by tsl.Transaction_Id, tsl.State_Id
If I understood you correctly you need to add StateId to grouping statement as well so that you will be able to select StateId and TransactionId.
So this should work:
var states = (from logs in _context.TransactionStatesLog
group logs by new { logs.TransactionId, logs.StateId } into g
select new
{
TransactionId = g.Key.TransactionId,
StateId = g.Key.StateId,
Timestamp = g.Max(x => x.Timestamp)
}).ToList();
See: Group by with multiple columns using lambda

Linq groupby and global count (not count per group)

I have the following query:
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost)
};
It returns all the incidents aggregated by month and year. But I need the total number of incidents too. Not the incidents for every group, but the total count. The sum of the incidents of all groups. Can this be done in this query or is it better to just roll another query just to get the total global count?
I believe you could get the sum per group and then sum those. Something like
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost),
Count = g1.Count()
};
var total = queryResult.SUM(x=>x.count);
The other alternative I see is to join a sub query see Join Subquery result in Linq
something like
var subquery = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
into g1
select new
{
Total = g1.Count()
};
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
join sub in subquery on 1=1
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost),
Count = sub.total
};

Linq Query with Max Effective Date

Having trouble understanding how i can convert the following SQL Query into LINQ. Specifically the MAX effective date parts.
SELECT A.NAME, X.XLATLONGNAME AS ACTION, C.DESCR, B.EFFDT
FROM ACTN_REASON_TBL C, XLATTABLE X, PERSONAL_DATA A, JOB B
WHERE A.EMPLID = B.EMPLID
AND B.ACTION = C.ACTION(+)
AND B.ACTION_REASON = C.ACTION_REASON(+)
AND (C.EFFDT = (SELECT MAX(C_ED.EFFDT) FROM ACTN_REASON_TBL C_ED
WHERE C.ACTION = C_ED.ACTION
AND C.ACTION_REASON = C_ED.ACTION_REASON
AND C_ED.EFFDT <= B.EFFDT)
OR C.EFFDT IS NULL)
AND X.FIELDNAME = 'ACTION'
AND B.ACTION = X.FIELDVALUE
AND X.EFFDT = (SELECT MAX(X_ED.EFFDT) FROM XLATTABLE X_ED
WHERE X.FIELDNAME = X_ED.FIELDNAME
AND X.LANGUAGE_CD = X_ED.LANGUAGE_CD
AND X.FIELDVALUE = X_ED.FIELDVALUE
AND X_ED.EFFDT <= SYSDATE)
AND B.ACTION_DT BETWEEN sysdate - 30 AND sysdate
AND B.ACTION NOT IN ('EOI','NBY','LIF','FSC','LOA','LTD','PLA','RFL','PAY')
ORDER BY A.NAME, B.EFFDT DESC
This is what i have so far for my LINQ query...
public ActionResult RecentTransaction()
{
RecentTransViewModel recTransModel = new RecentTransViewModel();
var minusThirty = DateTime.Today.AddDays(-90);
var today = DateTime.Now;
var exceptionList = new List<string> { "EOI", "NBY", "LIF", "FSC", "LOA", "LTD", "PLA", "RFL", "PAY" };
var transaction = (from p in recentTrans.PERSONAL_DATA
join j in recentTrans.JOB on p.EMPLID equals j.EMPLID
join a in recentTrans.ACTN_REASON_TBL on j.ACTION_REASON equals a.ACTION_REASON
join x in recentTrans.XLATTABLE_VW on j.ACTION equals x.FIELDVALUE
where x.FIELDNAME == "ACTION"
where j.ACTION_DT >= minusThirty
where j.ACTION_DT <= today
where !exceptionList.Contains(j.ACTION)
select new RecentTransViewModel
{
Name = p.NAME,
Action = x.XLATLONGNAME,
Descr = a.DESCR,
EffectiveDate = j.EFFDT
}).OrderBy(d => d.Name)
.ToList();
return View(transaction);
Any help is greatly appriciated!
LINQ allows you to add nested Queries within the same contex so that you can:
select new RecentTransViewModel
{
Name = p.NAME,
Action = x.XLATLONGNAME,
Descr = a.DESCR,
EffectiveDate = EffectiveDate = (from eft in recentTrans.XLATTABLE_VW
where eft.FIELDNAME == x.FIELDNAME AND eft.LANGUAGE_CD = X.LANGUAGE_CD AND
eft.FIELDVALUE = x.FIELDVALUE AND eft.EFFDT <= SYSDATE
select eft).Max(c=> c.EFFDT)
}).OrderBy(d => d.Name)
.ToList();

C# linq query OUTER APPLY with SUM

Trying to convert a SQL Query to Linq:
SELECT GrossInvoiceAmount, C.SumOfPayments
FROM invoice
OUTER APPLY(
SELECT SUM(SumOfPayments) AS SumOfPayments FROM vw_sumOfPayments
WHERE vw_sumOfPayments.PaymentDate = '01/01/2010 00:00:00'
AND vw_sumOfPayments.InvoiceId = invoice.InvoiceId
) C
WHERE LastTransmitDate = '01/01/2010 00:00:00'
I have this in my C# code. It runs and gives results, but the invoice.GrossInvoiceAmount amount is wrong. Any ideas? Thanks.
var InvoiceQuery = (from invoice in this.Context.Invoices
join payment in this.Context.vw_sumOfPayments.Where(pay => DbFunctions.TruncateTime(pay.PaymentDate) >= startdate
&& DbFunctions.TruncateTime(pay.PaymentDate) <= enddate)
on invoice.InvoiceID equals payment.InvoiceID into pays
select new InvoiceModel{
InvoiceTypeId = invoice.InvoiceTypeId,
InvoiceNumber = invoice.InvoiceNumber,
InvoiceDate = invoice.InvoiceDate,
InvoiceAmount = invoice.GrossInvoiceAmount,
PaymentAmount = pays.AsEnumerable().Sum(o => o.SumOfPayments)
});

Linq query with two sub-queries that group by, one with an average, and one with a max

I have a parent table, parentTable which may or may not have children in childTable. I am looking to get average % complete of any given parent's children, and the MAX(due) (date) of the children where they exist. My SQL is this:
SELECT parentRecord_id, assigned_to,
(SELECT avg(complete)
FROM childTable
WHERE parent_id = parentRecord_id
and deleted IS NULL
GROUP BY parent_id),
(SELECT max(due)
FROM childTable
WHERE parent_id = parentRecord_id
and deleted IS NULL
GROUP BY parent_id
)
FROM parentTable s
WHERE s.deleted IS NULL and assigned_to IS NOT NULL
My result set gives me rows with either correct values for the average and max, or null. In this instance I have to do follow up processing so I could ignore the null values if I was doing a foreach through DataTable rows. However I am trying to do this in Linq and can't figure out how to avoid a System.InvalidOperationException where Linq is trying to cast null to a double. Here is what I've tried so far.
var query8 = from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
((from t in db.childTable
where t.parent_id == s.strategy_id
group t by new { t.parent_id } into g
select new
{
a0 = g.Average(f0 => f0.complete )
}).FirstOrDefault().a0)
};
foreach (var itm in query8)
{
Console.WriteLine(String.Format("User id:{0}, Average: {1}", itm.ID, itm.Average));
}
Here's my question. How do I get the query to handle those returned rows where average complete or max due (date) are null?
You can either filter out the records where the values are null (by another condition) or if you want to include them do something like this:
a0 = g.Average(f0 => f0.complete.HasValue? f0.complete: 0 )
I would cast the list to nullable double before calling Average/Max like so:
var query8 =
from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
from t in db.childTable
where t.parent_id == s.strategy_id
group t by t.parent_id into g
select g.Cast<double?>().Average(f0 => f0.complete)
};
Assuming complete is a Nullable, you should be able to do:
var query8 = from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
((from t in db.childTable
where t.parent_id == s.strategy_id
&& s.complete.HasValue()
group t by new { t.parent_id } into g
select new
{
a0 = g.Average(f0 => f0.complete )
}).FirstOrDefault().a0)
};
Thanks to all who responded.
I was unable get around the null anonymous issue with the basic query as I had it, but adding a join to the childTable eliminated the nulls.
Another solution is to use a from x in g.DefaultIfEmpty clause.
var query8 =
from st in db.tableParent
select new { Ass = st.assigned_to ,
Avg =
(from ta in db.tableChild
group ta by ta.parent_id into g
from x in g.DefaultIfEmpty()
select g.Average((f0=>f0.complete))).FirstOrDefault()
};

Resources