SELECT and SUM in Entity Framework - sql

I want to sum all the columns and where context between dates, basically I want to convert the below SQL query to EF:
select meterCategory, sum(cost) maxCost
from [dbo].[UsageData]
where date between '2019-06-25' and '2019-06-25' and
cost >= 1
group by meterCategory
order by maxCost desc

(var startDate, var endDate) = (new DateTime(2019, 6, 25), new DateTime(2019, 6, 25));
var result =
await dbContext.UsageDatas
.Where(ud => ud.Cost >= 1 && ud.Date >= startDate && ud.Date <= endDate)
.GroupBy(ud => ud.MeterCategory)
.Select(g => new { MeterCategory = g.Key, MaxCost = g.Sum(c => c.Cost) })
.OrderByDescending(g => g.MaxCost)
.ToListAsync();
You can also use tuples and even name their properties, instead of anonymous classes.
Or since you're SQL oriented, you might want to use C# LINQ query syntax (this one uses tuple):
var query =
from ud in dbContext.UsageDatas
where ud.Cost >= 1 && ud.Date >= startDate && ud.Date <= endDate
group ud by ud.MeterCategory into g
select (MeterCategory: g.Key, MaxCost: g.Sum(ud => ud.Cost)) into r
orderby r.MaxCost descending
select r;
var result = await query.ToListAsync();

Related

Query syntax in entity framework

I'm doing a query (see below), but I do not know how to retrieve all data from a select.
var model = new dbContext();
var query = from mp in model.matiere_premiere join req in (from stk in model.stock_mp
join ms in model.matiere_premiere
on stk.matiere_premiere_code equals
ms.code
where stk.date <= DateTime.Today
orderby stk.date descending
select new new { stk.qte, stk.matiere_premiere_code })
on mp.code equals req.matiere_premiere_code
group mp by new { mp.code } into grp
orderby grp.Key
select new
{
grp.Key,
grp.First().designation,
grp.Last().frns
};
The equivalent sql query is:
SELECT matiere_premiere.code,matiere_premiere.designation,
"matiere_premiere.unite, matiere_premiere.frns ,IF(ISNULL(REQ.qte), '0.00', REQ.qte) AS qte
FROM matiere_premiere LEFT JOIN (SELECT qte,matiere_premiere_code FROM stock_mp
JOIN matiere_premiere ON matiere_premiere.code = matiere_premiere_code
WHERE DATE <= CURRENT_DATE() ORDER BY DATE DESC)
AS REQ ON REQ.matiere_premiere_code = matiere_premiere.code
GROUP BY matiere_premiere.code ORDER BY matiere_premiere.code
it's simple, the group is also an enumerator, so you should return
select grp;
then, for each group, you can do a foreach of the values
foreach(var group in query)
{
Console.WriteLine("Key: " + group.Key);
foreach(var v in group)
{
Console.WriteLine("Value: " + v.Property);
}
}

How to convert this Sql query to LINQ query

can any one to help to convert this below given sql query to linq.
exec sp_executesql N'use db;
WITH Members AS
(
select ROW_NUMBER() OVER (ORDER BY id DESC) as row, num
from tbl
)
Select row, num
from Members
where row BETWEEN #InitialRow AND #EndRow order by row ASC;',N'#InitialRow int,#EndRow int',#InitialRow=0,#EndRow=5
--
thanks,
Here is a way to to it (with #InitialRow=0,#EndRow=5) :
var result = Members
.OrderByDescending(x => x.id)
.Take(5)
.Select((x,i) =>
new { row = i, num = x.num });
With any values:
var result = Members
.OrderByDescending(x => x.id)
.Skip(InitialRow)
.Take(EndRow-InitialRow)
.Select((x,i) =>
new { row = i+InitialRow, num = x.num });

Linq Sum based on some columns

I have a query returning some values for a specific CompanyId and a Specific Month/Year.
I want to make a report for the whole year. (So I need to sum up the values from different months, but of the same year and CompanyId)
Here is my query now:
from er in EconomicReports
join com in Companies on er.CompanyId equals com.Id
join cou in Countries on com.CountryId equals cou.Id
where er.Year == 2014
select new
{
Country = cou.Name,
CompanyId = com.Id,
CorporationId = com.CorporationId,
Year = er.Year,
RegisteredCases = er.NewCasesTotalCount,
RegisteredCasesAmount = er.NewCasesTotalAmount,
ResolvedCases = er.ClosedCasesTotalCount,
ResolvedCasesAmount = er.ClosedCasesCapitalAmount + er.ClosedCasesInterestAmount,
ActiveCases = (er.NewCasesTotalCount ?? 0) - (er.ClosedCasesTotalCount ?? 0),
ActiveCasesAmount = (er.NewCasesTotalAmount ?? 0) - (er.ClosedCasesCapitalAmount ?? 0) - (er.ClosedCasesInterestAmount ?? 0)
}
Basically, rows 1 and 5 need to be one row, because they are from the same year, same company Id (I have put also Month in the results for you to see that is a different month, but same year)
you need a GroupBy:
var query = (from er in EconomicReports
join com in Companies on er.CompanyId equals com.Id
join cou in Countries on com.CountryId equals cou.Id
where er.Year == 2014
select new
{
Country = cou.Name,
CompanyId = com.Id,
CorporationId = com.CorporationId,
Year = er.Year,
RegisteredCases = er.NewCasesTotalCount,
RegisteredCasesAmount = er.NewCasesTotalAmount,
ResolvedCases = er.ClosedCasesTotalCount,
ResolvedCasesAmount = er.ClosedCasesCapitalAmount + er.ClosedCasesInterestAmount,
ActiveCases = (er.NewCasesTotalCount ?? 0) - (er.ClosedCasesTotalCount ?? 0),
ActiveCasesAmount = (er.NewCasesTotalAmount ?? 0) - (er.ClosedCasesCapitalAmount ?? 0) - (er.ClosedCasesInterestAmount ?? 0)
});
var result = query
.GroupBy(u => new {u.Country, u.CompanyId, u.CorporationId, u.Year})
.Select(u => new
{
Country = u.Key.Country,
CompanyId = u.Key.CompanyId,
CorporationId = u.Key.CorporationId,
Year = u.Key.Year,
RegisteredCases = u.Select(t => t.RegisteredCases).DefaultIfEmpty().Sum(),
RegisteredCasesAmount = u.Select(t => t.RegisteredCasesAmount).DefaultIfEmpty().Sum(),
ResolvedCases = u.Select(t => t.ResolvedCases).DefaultIfEmpty().Sum(),
ResolvedCasesAmount = u.Select(t => t.ResolvedCasesAmount).DefaultIfEmpty().Sum(),
ActiveCases = u.Select(t => t.ActiveCases).DefaultIfEmpty().Sum(),
ActiveCasesAmount = u.Select(t => t.ActiveCasesAmount).DefaultIfEmpty().Sum()
})
.ToList();
i think Country and CorporationId must be same for grouped records based on CompanyId and Year, so i was included them in group by, to use in Select

Join Subquery result in Linq

I am posting one more doubt of mine:
Is there a way by which we can use the result of one query and then join the same further just like we do in SQL:
SELECT Applications.* , ApplicationFees.ApplicationNo, ApplicationFees.AccountFundDate1,ApplicationFees.AccountFundDate2 ,ApplicationFees.AccountFundDate3 , ApplicationFees.AccountCloseDate1, ApplicationFees.AccountCloseDate2,ApplicationFees.AccountCloseDate3,
isnull(SUBQRY11.AMNT ,0) as SCMSFEE1R,
isnull(SUBQRY12.AMNT,0) as SCMSFEE2R,
Left Join
(
SELECT ApplicationNo,COUNT(ApplicationNo) AS CNT, SUM(Amount) as AMNT
FROM Payments where (FEETYPE=1 AND FeePosition=1) and (FeeDate>='2011-01-01')
and (FeeDate<='2012-01-01')
GROUP BY ApplicationNo
)SUBQRY11
ON ApplicationFees.ApplicationNo= SUBQRY11.ApplicationNo
Left Join
(
SELECT ApplicationNo,COUNT(ApplicationNo) AS CNT2, SUM(Amount) as AMNT
FROM Payments where (FEETYPE=1 AND FeePosition=2) and (FeeDate>='2011-01-01')
and (FeeDate<='2012-01-01')
GROUP BY ApplicationNo )SUBQRY12 ON ApplicationFees.ApplicationNo=SUBQRY12.ApplicationNo
I want to avoid the same in foreach of the query as that will be quite time consuming.
Yes, you can join sub queries. Like this:
var query = from f in db.ApplicationFees
join sub in (from p in db.Payments
where p.Type == 1 && p.Position == 1 &&
p.Date >= fromDate && p.Date <= toDate
group p by p.ApplicationNo into g
select new {
ApplicationNo = g.Key,
CNT = g.Count(),
AMNT = g.Sum(x => x.Amount)
})
on f.ApplicationNo equals sub.ApplicationNo into feePayments
select new { Fee = f, Payments = feePayments };
But writing it in single query is not very maintainable. Consider to compose your query from sub-queries defined separately:
var payments = from p in db.Payments
where p.Type == 1 && p.Position == 1 &&
p.Date >= fromDate && p.Date <= toDate
group p by p.ApplicationNo into g
select new {
ApplicationNo = g.Key,
CNT = g.Count(),
AMNT = g.Sum(x => x.Amount)
};
var query = from f in db.ApplicationFees
join p in payments
on f.ApplicationNo equals p.ApplicationNo into feePayments
select new { Fee = f, Payments = feePayments };

dynamically change LINQ to Entity query

int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum( ODInfo => ODInfo.TONS2009)
};
IN the expression 'ODInfo => ODInfo.TONS2009', how do I change TONS2009 to TONS2010 or TONS2011 based on the method parameter 'int year' ?
K06a's answer is close but won't work server-side. Try this:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Expression<Func<InputType,decimal>> expr)
{
return from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.AsQueryable().Sum(expr)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
I haven't tried this, but did something similar here.
UPDATE
If you really need to translate input strings (like "2009") to expressions, it's still possible:
string year = "2009";
Type ODInfoType = typeof(ODINFOTYPE); // substitute with the type of ODInfo
ParameterExpression pe = ParameterExpression.Parameter(ODInfoType, "ODInfo");
MemberInfo mi = ODInfoType.GetProperty("TONS" + year);
MemberExpression me = Expression.MakeMemberAccess(pe, mi);
var expr = Expression.Lambda<Func<ODINFOTYPE, decimal>>(me, pe);
Be aware that this is a patch to the extremly evil structure of your database.
You can try something like that:
TotalTons = g.Sum( ODInfo => (year == 2009) ? ODInfo.TONS2009 : ((year == 2010)
? ODInfo.TONS2010 : ODInfo.TONS2011))
Or make it more readable and use { } to split that lambda expression into more then one line and use eg. switch statement.
The best solution is to break this up into multiple querys that you can compose to a final query:
int year = 2009; // get summ of TONS2009 column
var odInfos =
year == 2009 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2009 })
year == 2010 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2010 })
year == 2011 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2011 })
: null;
var query = from ODInfo in odInfos
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum(ODInfo => ODInfo.TONS)
};
This will specialize to three possible queries at runtime, thereby giving the best possible performance. It is better than a case-switch.
Try this way:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Func<InputType,decimal> func)
{
return from ODInfo in data
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.Sum(func)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
Using DynamicLinq which works with EF also:
int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select g;
var projectedGroups = query.Select("new (Key as OID, Sum(TONS" + year + ") as TotalTons)");