Convert SUM / CASE WHEN / GROUP BY SQL query into LINQ in C# - c#

I have a table that has some value. I want to group by date month, My date field name is ApplicatonDate, and some enum values.
Here is a SQL query which I need to convert it into Linq:
SELECT
Count(he.ApplicationDate) AS Total,
MAX(he.ApplicationDate) AS ApplicationDate,
SUM(CASE WHEN he.Status=0 then 1 else 0 end )AS Last12MonthTotalPending,
SUM(CASE WHEN he.Status = 1 THEN 1 ELSE 0 END )AS Last12MonthTotalApproved,
SUM(CASE WHEN he.Status = 2 THEN 1 ELSE 0 END )AS Last12MonthTotalDenied
FROM dbo.Client_Heatings he
WHERE he.ApplicationDate >= DATEADD(Year,-1,GETDATE()) AND he.ApplicationDate <= GETDATE()
GROUP BY CAST(ApplicationDate AS DATE)
This is my LINQ. That is my WRONG LINQ.
var result = heatingList
.Where(r => r.Id == 123)
.GroupBy(r =>r.ApplicationDate )
.Select(grp => new HeatingApplicationSummaryDTO
{
Last12MonthTotalPending = grp.Sum( t => t.Status == 0 ? 1 : 0),
Last12MonthTotalApproved = grp.Sum( t => t.Status == 1 ? 1 : 0),
Last12MonthTotalDenied = grp.Sum( t => t.Status == 2 ? 1 : 0),
Total = grp.Sum(c => c.Status)
}).ToList();
I can't convert SQL query to LINQ query. I do not know how to convert and implement SUM/CASE section with Linq.
Thanks in advance

Try the following query:
var endDate = DateTime.Now;
var startDate = endDate.AddYears(-1);
var result = heatingList
.Where(r => r.ApplicationDate >= startDate && r.ApplicationDate <= endtDate)
.GroupBy(r => r.ApplicationDate.Date)
.Select(grp => new HeatingApplicationSummaryDTO
{
Total = grp.Count(),
ApplicationDate = grp.Max(t => t.ApplicationDate),
Last12MonthTotalPending = grp.Sum(t => t.Status == 0 ? 1 : 0),
Last12MonthTotalApproved = grp.Sum(t => t.Status == 1 ? 1 : 0),
Last12MonthTotalDenied = grp.Sum(t => t.Status == 2 ? 1 : 0),
}).ToList();

Related

Entity Framework grouped query translated into multiple SELECTs

Can someone help and tell me why this LINQ query is being translated in EF to multiple SELECTs ?
var query = db.ReportedFulfillments.GroupBy(x => x.ContractId).Select(grouping => new
{
ContractId = grouping.Key,
Total = grouping.Count(),
FU = grouping.Count(x => x.Month == 1 && x.Value == 1),
BR = grouping.Count(x => x.Month == 1 && x.Value == 2)
}
I thought that EF will output something like this:
SELECT ContractId,
Count(*) AS Total,
COUNT(CASE WHEN [Month] = 1 AND [Value] = 1 THEN Value END) AS FU,
COUNT(CASE WHEN [Month] = 1 AND [Value] = 2 THEN Value END) AS BR,
FROM ReportedFulfillments GROUP BY ContractId
I'm using EntityFramework 6.2.0
Shortly, LINQ conditional Count of the GroupBy grouping result set is not supported very well (the SQL you expect relies on SQL COUNT(expr) which excludes NULLs and has no LINQ equivalent).
But the equivalent conditional Sum is supported and translated just well, so instead of
FU = grouping.Count(x => x.Month == 1 && x.Value == 1),
BR = grouping.Count(x => x.Month == 1 && x.Value == 2)
use
FU = grouping.Sum(x => x.Month == 1 && x.Value == 1 ? 1 : 0),
BR = grouping.Sum(x => x.Month == 1 && x.Value == 2 ? 1 : 0)

Sum multiple column in LINQ

How can I reproduce this query using linq?
SELECT
SUM(SecondsSinceLastEvent) as AccessTime,
SUM (case when DownloadType = 0 then 1 end) FileDownloadCount,
SUM (case when DownloadType = 1 then 1 end) KortextDownloadCount,
SUM (case when Action = 'print' then 1 end) PrintCountFROM EReaderLogs WHERE PublishedContent_Id = XXX
In LINQ to Entities you need to first use GroupBy before using multiple aggregate functions. To sum all the elements in a column you can group by some static key so then a single group would be a whole table:
var query = context.EReaderLogs
.Where(e => e.PublishedContent_Id == someValue)
.GroupBy(a => 1, (k, g) =>
{
AccessTime = g.Sum(e => e.SecondsSinceLastEvent),
FileDownloadCount = g.Sum(e => e.DownloadType == 0 ? 1 : 0),
KortextDownloadCount = g.Sum(e => e.DownloadType == 1 ? 1 : 0),
PrintCount = g.Sum(e => e.Action == "print" ? 1 : 0)
});

Comparing SQL Date Time in Entity Framework by date parts

I have this query that was actually a view but I wanted to control the WHERE clause so I decided to tackle it by LINQ and EF6:
SELECT NULLIF(SUM([a].[REQUESTED_QTY]), 0) AS [Transaction],
NULLIF(SUM([a].[ITEM_TOTAL]), 0) AS [Income]
FROM [dbo].[BILL_INFO_DETAIL] AS [a]
INNER JOIN [dbo].[SERVICE_INFO] AS [b]
ON [a].[SERVICE_CODE] = [b].[SERVICE_CODE]
WHERE ([a].[INPUT_STATUS] = '1')
AND ([b].[SERVICE_CODE] IN ( 1610, 1611, 1612 ))
AND ([a].[PAY_MODE_ID] IN ( 1, 2 ))
AND (CONVERT(VARCHAR(2), a.STAMP_DATE, 101) IN ( '10', '11', '12' ))
AND (CONVERT(VARCHAR(4), a.STAMP_DATE, 102) IN ( '2017' ))
AND ([b].[FEE] > 1)
After a while of trial and error, I got this conversion:
public async Task<ViewGuessOnline> GetGuessOnline(int[] serviceCodes, byte[] payModes, string[] months, string year)
{
try
{
using (var context = new FinanceConnection())
{
var resultTemp = from a in
(from a in context.BILL_INFO_DETAILS
where
a.INPUT_STATUS == true &&
serviceCodes.Contains(a.SERVICE_INFO.SERVICE_CODE) &&
payModes.Contains(a.PAY_MODE_ID) &&
months.Contains(SqlFunctions.StringConvert(a.STAMP_DATE)) &&
(new[] {"2017"}).Contains(SqlFunctions.StringConvert(a.STAMP_DATE)) &&
a.SERVICE_INFO.FEE > 1
select new
{
a.REQUESTED_QTY,
a.ITEM_TOTAL,
Dummy = "x"
})
group a by new {a.Dummy}
into g
select new
{
Transaction = g.Sum(p => p.REQUESTED_QTY) == 0 ? (int?) null : (int) g.Sum(p => p.REQUESTED_QTY),
Income = g.Sum(p => p.ITEM_TOTAL) == 0 ? (decimal?) null : (decimal) g.Sum(p => p.ITEM_TOTAL)
};
// result = (await result.OrderByDescending(o => o.CustomerCode).ToListAsync()).AsQueryable();
}
Logger.Info($"{LayerName} -> {callerInfo.MethodName} -> Returning");
return result;
}
catch (Exception exp)
{
Logger.Error($"{LayerName} -> {callerInfo.MethodName} -> Exception [{exp.Message}]", exp);
throw;
}
}
I'm having an issue with the date part. In original SQL, the comparison of a Quarter 4 of 2107 is very easy. But I cannot find a proper inline linq conversion that translates to proper SQL.
Also, I had to use a dummy grouping even thoug there is not groups in the original SQL.
A bit more wall-baning and I was able to make this work:
from a in
(from a in db.BILL_INFO_DETAIL
where
a.INPUT_STATUS == true &&
(new int[] {1610, 1611, 1612 }).Contains(a.SERVICE_INFO.SERVICE_CODE) &&
(new int[] {1, 2 }).Contains(a.PAY_MODE_ID) &&
a.STAMP_DATE != null &&
(new int[] {10, 11, 12 }).Contains(a.STAMP_DATE.Value.Month) &&
a.STAMP_DATE.Value.Year == 2017 &&
a.SERVICE_INFO.FEE > 1
select new
{
a.REQUESTED_QTY,
a.ITEM_TOTAL,
Dummy = "x"
})
group a by new {a.Dummy}
into g
select new
{
Transaction = g.Sum(p => p.REQUESTED_QTY) == 0 ? (int?) null : (int) g.Sum(p => p.REQUESTED_QTY),
Income = g.Sum(p => p.ITEM_TOTAL) == 0 ? (decimal?) null : (decimal) g.Sum(p => p.ITEM_TOTAL)
}
I changed the method's parameter datatypes accordingly and this works now.

Converting Sql to Linq using group get wrong result

I have a query where I need to get the count of Commpliance and NonCompliance
Here is the SQL version I need this to convert to linq...
select ScheduleClause,
COUNT(case Compliance
when 1 then 1 end) Compliance,
Count(case Compliance
when 0 then 1 end) NonCompliance
from Compliance_RiskRegisterEntry cr
where cr.RiskRegisterTypeId = 1 and Auditor = 5508 and MONTH(AuditDate) = 10 and YEAR(AuditDate) = 2013
group by ScheduleClause
I try this linq query but I get different result
compliance
.GroupBy(x => new
{
x.ScheduleClause, x.Compliance
})
.Where(x => x.Key.Compliance == 1)
.Select(x => new RiskRegisterCompliancePerCategoryDto
{
ScheduleClause = x.Key.ScheduleClause,
Compliant = x.Key.Compliance == 1 ? 1 : 0,
NonCompliant = x.Key.Compliance == 0 ? 1 : 0,
GrandTotal = x.Count()
}).ToList();
It depends on your exact column definitions. Here I'm using
Create Table Compliance_RiskRegisterEntry (
ScheduleClause varchar(10),
AuditDate datetime not null,
RiskRegisterTypeID int not null,
Auditor Int,
Compliance bit not null
);
With this, the following Linq works:
compliance
.Where(p => p.RiskRegisterTypeID == 1 &&
p.Auditor == 5508 &&
p.AuditDate.Month == 10 &&
p.AuditDate.Year == 2013
)
.GroupBy(x => x.ScheduleClause)
.Select(x => new {
ScheduleClause = x.Key,
Compliant = x.Sum(y => y.Compliance ? 1 : 0),
NonCompliant = x.Sum(y => y.Compliance ? 0 : 1),
GrandTotal = x.Count()
});
Instead of x.Sum(...) you can use x.Count(y.Compliance), but the generated query for this looks much worse than using Sum
compliance
.Where(p=> p.RiskRegisterTypeId = 1 && p.Auditor = 5508 &&
SqlFunctions.DatePart("MM", p.AuditDate) = 10 &&
SqlFunctions.DatePart("yy", p.AuditDate) = 2013)
.GroupBy(x => x.ScheduleClause)
.Select(x => new RiskRegisterCompliancePerCategoryDto
{
ScheduleClause = x.Key.ScheduleClause,
Compliant = x.Key.Compliance == 1 ? 1 : 0,
NonCompliant = x.Key.Compliance == 0 ? 1 : 0,
GrandTotal = x.Count()
}).ToList();

LINQ Query with both CASE statement and SUM function

I'm struggling to find an example of how to return a conditional sum using a LINQ query or LAMBDA. I've written both independently but combining the CASE with SUM is vexing. I'm tempted to "cheat" and use a SQL view, but thought I'd ask first. I greatly appreciate any suggestions. Here's my SQL that I'm looking to convert.
SELECT p.product_name,
SUM(CASE WHEN o.order_dt <= getdate() - 1 THEN o.quantity END) AS volume_1day,
SUM(CASE WHEN o.order_dt <= getdate() - 7 THEN o.quantity END) AS volume_7day,
SUM(CASE WHEN o.order_dt <= getdate() - 30 THEN o.quantity END) AS volume_30day,
SUM(o.quantity) AS volume_all
FROM products p left outer join orders o on p.product_id = o.product_id
GROUP BY p.product_name
Here is an example using the Northwinds database. This will get you the results that you are expecting but the SQL won't match your example.
using (var context = new NorthwindEntities())
{
DateTime volumn1Date = DateTime.Today.AddDays(-1);
DateTime volumn7Date = DateTime.Today.AddDays(-7);
DateTime volumn30Date = DateTime.Today.AddDays(-30);
var query = from o in context.Order_Details
group o by o.Product.ProductName into g
select new
{
ProductName = g.Key,
Volume1Day = g.Where(d => d.Order.OrderDate.Value <= volumn1Date)
// cast to Int32? because if no records are found the result will be a null
.Sum(d => (Int32?) d.Quantity),
Volume7Day = g.Where(d => d.Order.OrderDate.Value <= volumn7Date)
.Sum(d => (Int32?) d.Quantity),
Volume30Day = g.Where(d => d.Order.OrderDate.Value <= volumn30Date)
.Sum(d => (Int32?) d.Quantity)
};
var list = query.ToList();
}
answer does not wrong but does not generate optimize query. better answer is:
using (var context = new NorthwindEntities())
{
DateTime volumn1Date = DateTime.Today.AddDays(-1);
DateTime volumn7Date = DateTime.Today.AddDays(-7);
DateTime volumn30Date = DateTime.Today.AddDays(-30);
var query = from o in context.Order_Details
group o by o.Product.ProductName into g
select new
{
ProductName = g.Key,
Volume1Day = g.Sum(d => d.Order.OrderDate.Value <= volumn1Date ? (Int32?) d.Quantity : 0),
Volume7Day = g.Sum(d => d.Order.OrderDate.Value <= volumn7Date ? (Int32?) d.Quantity : 0),
Volume30Day = g.Sum(d => d.Order.OrderDate.Value <= volumn30Date ? (Int32?) d.Quantity : 0)
};
var list = query.ToList();
}

Categories

Resources