Implementing the min & max function with group by and having in linq - c#

I have this below sql query which i have to implement the same using linq but i am unable to create a linq statement for the same. Please provide some help.
Declare #DateTime datetime
set #DateTime = '06/20/2012';-- To get the yesterday/any day attendance record.
With Cte
as
(
Select EmployeeiD,(convert(varchar,(DATEDIFF(MINUTE,Min(DateAndTime),Max(DateAndTime))/60)) + ':'
+ convert(varchar,(DATEDIFF(MINUTE,Min(DateAndTime),Max(DateAndTime))/60)))WorkedHoursAndMinutes,
Min(DateAndTime) As DateAndTime from EmployeeAttendance group by
EmployeeiD,convert(varchar,DateAndTime,101),convert(varchar,DateAndTime,101) having (
(DATEDIFF(MINUTE,Min(DateAndTime),Max(DateAndTime))/60) >= 12
or (DATEDIFF(MINUTE,Min(DateAndTime),Max(DateAndTime))) = 0 )
)
select * from Cte
where convert(varchar,Cte.DateAndTime,103) =
(select convert(varchar,#DateTime,103));

As you only show how you tried to achieve something it's kinda hard to exactly infer what you want. But I think this is it:
employeeAttendances.Where(e => e.DateAndTime >= d1 && e.DateAndTime < d2)
.GroupBy (e => e.EmployeeiD)
.Select (g => new
{
g.Key,
Diff = EntityFunctions.DiffHours(g.Min(x => x.DateAndTime),
g.Max(x => x.DateAndTime))
} )
.Where(a => a.Diff > 12 || a.Diff == 0)
It calculates the difference between the minimum and maximum time on one specific day for each EmployeeID.

Related

SQL Server query with subquery to LINQ query

I wrote a SQL query that will get the count of tickets, closed tickets and its closure rate (%) and group it monthly basis (current year), but I would like to express this as a LINQ query to achieve the same result.
SELECT *, (ClosedCount * 100 / TicketCount) AS ClosureRate FROM (
SELECT COUNT(Id) as TicketCount, MONTH(InsertDate) as MonthNumber, DATENAME(MONTH, E1.InsertDate) as MonthName,
(SELECT COUNT(Id) FROM EvaluationHistoryTable E2 WHERE TicketStatus = 'CLOSED' AND YEAR(E2.InsertDate) = '2021') AS 'ClosedCount'
FROM EvaluationHistoryTable E1
WHERE YEAR(E1.InsertDate) = 2021
GROUP BY MONTH(InsertDate), DATENAME(MONTH, E1.InsertDate));
This is code that I'm working on:
var ytdClosureRateData = _context.EvaluationHistoryTable
.Where(t => t.InsertDate.Value.Year == DateTime.Now.Year)
.GroupBy(m => new
{
Month = m.InsertDate.Value.Month
})
.Select(g => new YtdTicketClosureRateModel
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedMonthName(g.Key.Month),
MonthNumber = g.Key.Month,
ItemCount = g.Count(),
ClosedCount = // problem
ClosureRate = // problem
}).AsEnumerable()
.OrderBy(a => a.MonthNumber)
.ToList();
I am having rtouble trying to express the count of closed tickets (ClosedCount) in linq format, I need the count to calculate the ClosureRate.
This won't be the same SQL but it should produce the same result in memory:
var ytdClosureRateData = _context.EvaluationHistoryTable
.Where(t => t.InsertDate.Value.Year == DateTime.Now.Year)
.GroupBy(m => new
{
Month = m.InsertDate.Value.Month
})
.Select(g => new
{
Month = g.Key.Month,
ItemCount = g.Count(),
ClosedCount = g.Where(t => t.TicketStatus == "CLOSED").Count()
}).OrderBy(a => a.MonthNumber)
.ToList()
.Select(x => new YtdTicketClosureRateModel
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedMonthName(x.Month),
MonthNumber = x.Month,
ItemCount = x.ItemCount,
ClosedCount = x.ClosedCount,
ClosureRate = x.ClosedCount * 100D / x.ItemCount
})
.ToList();
Two techniques have been implemented here:
Use Fluent Query to specify the filter to apply for the ClosedCount set, you can combine Fluent and Query syntax to your hearts content, they each have pros and cons, in this instance it just simplifed the syntax to do it this way.
Focus the DB query on only bringing back the data that you need, the rest can be easily calculated in member after the initial DB execution. That is why there are 2 projections here, the first should be expressed purely in SQL, the rest is evaluated as Linq to Objects
The general assumption is that traffic over the wire and serialization are generally the bottle necks for simple queries like this, so we force Linq to Entities (or Linq to SQL) to produce the smallest payload that is practical and build the rest or the values and calculations in memory.
UPDATE:
Svyatoslav Danyliv makes a really good point in this answer
The logic can be simplified, from both an SQL and LINQ perspective by using a CASE expression on the TicketStatus to return 1 or 0 and then we can simply sum that column, which means you can avoid a nested query and can simply join on the results.
Original query can be simplified to this one:
SELECT *,
(ClosedCount * 100 / TicketCount) AS ClosureRate
FROM (
SELECT
COUNT(Id) AS TicketCount,
MONTH(InsertDate) AS MonthNumber,
DATENAME(MONTH, E1.InsertDate) AS MonthName,
SUM(CASE WHEN TicketStatus = 'CLOSED' THEN 1 ELSE 0 END) AS 'ClosedCount'
FROM EvaluationHistoryTable E1
WHERE YEAR(E1.InsertDate) = 2021
GROUP BY MONTH(InsertDate), DATENAME(MONTH, E1.InsertDate));
Which is easily convertible to server-side LINQ:
var grouped =
from eh in _context.EvaluationHistoryTable
where eh.InsertDate.Value.Year == DateTime.Now.Year
group eh by new { eh.InsertDate.Value.Month }
select new
{
g.Key.Month,
ItemCount = g.Count(),
ClosedCount = g.Sum(t => t.TicketStatus == "CLOSED" ? 1 : 0)
};
var query =
from x in grouped
orderby x.Month
select new YtdTicketClosureRateModel
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedMonthName(x.Month),
MonthNumber = x.Month,
ItemCount = x.ItemCount,
ClosedCount = x.ClosedCount,
ClosureRate = x.ClosedCount * 100D / x.ItemCount
};
var result = query.ToList();

converting SQL Group By to EF LINQ Lambda

I have this query
SELECT
DISTINCT TOP 10 COUNT([ServicesTracking].[dbo].[SearchLogs].[recordCount]) AS [RecordCount],
[ServicesTracking].[dbo].[SearchLogs].[searchValue] AS [SearchValue] FROM [ServicesTracking].[dbo].[SearchLogs]
WHERE (([ServicesTracking].[dbo].[SearchLogs].[searchType] = 'something'
AND [ServicesTracking].[dbo].[SearchLogs].[searchValue] <> ''
AND [ServicesTracking].[dbo].[SearchLogs].[recordCount] > 0
AND [ServicesTracking].[dbo].[SearchLogs].[appDomain] = 'localhost'))
GROUP BY [ServicesTracking].[dbo].[SearchLogs].[searchValue]
ORDER BY RecordCount DESC
And I am trying to convert it into EF LINQ Lambda. This is what I came up with. EDIT: Fixed a bug with my successive queries.
IQueryable<SearchLog> query = _context.SearchLogs
.Where(sl => sl.appDomain == AppDomain)
.Where(sl => sl.searchType == SearchType)
.Where(sl => sl.searchValue != string.Empty);
// Are we looking for terms that brought back results?
if (_greaterThanZero) query = query.Where(sl => sl.recordCount > 0);
else query = query.Where(sl => sl.recordCount == 0);
// Date range being used?
if (StartDate != DateTime.MinValue) query = query.Where(sl => sl.createDate > DateUtilities.GetStartOfDay(StartDate));
if (EndDate != DateTime.MinValue) query = query.Where(sl => sl.createDate < DateUtilities.GetEndOfDay(EndDate));
List<SearchResultSet> results = query
.GroupBy(sl => sl.searchValue)
.Select(sl => new SearchResultSet
{
SearchValue = sl.Key,
RecordCount = sl.Select(r => r.recordCount)Distinct().Count()
})
.OrderByDescending(sl => sl.RecordCount)
.Take(10)
.ToList();
foreach (SearchResultSet result in results)
result.SearchValue = HttpContext.Current.Server.UrlDecode(result.SearchValue);
return results;
It's not returning the same results. I'm fairly certain I have something mixed up in the GroupBy or Select statements. Any ideas?
Just
RecordCount = sl.Select(r => r.recordCount).Count()
You don't need the Distinct(), and it is not the same as the DISTINCT in the SQL Query, which does nothing, since each row has a distinct searchValue after GROUP BY searchValue.
But why
COUNT([ServicesTracking].[dbo].[SearchLogs].[recordCount]) AS [RecordCount]
? You are counting the rows with non-null recordCount. Should this be SUM()?

Grouping by day in LINQ without TruncateTime()

I have the following LINQ query for a MySQL database in a C# Razor MVC project.
private Dictionary<DateTime?, int> getOrderQuantityDict(DateTime start, DateTime end, int siteCode)
{
return (from o in thisDataEntities.this_table
where o.created_at >= start
&& o.created_at <= end
&& o.store_id == siteCode
select new { OrderDate = o.created_at, Id = o.entity_id})
.GroupBy(q => q.OrderDate)
.ToDictionary(q => q.Key, q => q.Count());
}
I need to group by day. Right now q.OrderDate has hours, minutes, and seconds. I need to ignore those when grouping.
The tricky part: I need to do this without TruncateTime(). When our host moved our DB, we lost the ability to use TruncateTime() for some reason. Our host has been less than helpful on this issue, and I'm hoping a workaround is possible.
Haven't tested it but the following may help you:
return (from o in thisDataEntities.this_table
where o.created_at >= start
&& o.created_at <= end
&& o.store_id == siteCode
select new { OrderDate = o.created_at, Id = o.entity_id})
.AsEnumerable() //Once this is executed, the database will return the result of the query and any other statement after this will be ran locally so TruncateTime will not be an issue
.GroupBy(q => q.OrderDate)
.ToDictionary(q => q.Key, q => q.Count());
You can convert date to the string and make grouping based on the string representation of date.
return
thisDataEntities.this_table
.Where(o => o.created_at >= start)
.Where(o => o.created_at <= end)
.Where(o => o.store_id == siteCode)
.Select(o => new
{
OrderDate = o.created_at,
Id = o.entity_id,
OrderDateFormatted =
SqlFunctions.DateName("yyyy", o.created_at) + "-" +
SqlFunctions.DateName("mm", o.created_at) + "-" +
SqlFunctions.DateName("dd", o.created_at)
})
.GroupBy(n => n.OrderDateFormatted) // format "2017-10-03"
.ToDictionary(g => g.First().OrderDate, g => g.Count());
With approach above execution should happened on database side. Of course only in case GroupBy supported.

How do I implement tsql's sum(...) over () in linq to SQL?

I have this functional t-sql query that counts the entries in a group by clause, and at the same time produces a percentage of the count compared to the entire set.
It is blazing fast (~90 ms) in Azure. I'd like to implement in a similar manner with LINQ to SQL, but I can't figure it out...
select f.worktype, f.counted, (100.0 * f.counted)/ (sum(f.counted) over ()) as percentage
from
(SELECT
wa.skillEN AS workType,
count(wa.skillEN) counted
FROM [dbo].WorkAssignments as WA
join [dbo].WorkOrders as WO ON (WO.ID = WA.workorderID)
WHERE wo.dateTimeOfWork < ('1/1/2014')
and wo.dateTimeOfWork > ('1/1/2013')
and wo.statusEN = 'Completed'
group by wa.skillEN) as f
group by f.worktype, f.counted
The LINQ I've been trying in LINQPad...
WorkAssignments
.Where(wa => wa.WorkOrder.DateTimeofWork > DateTime.Now.AddYears(-2)
&& wa.WorkOrder.DateTimeofWork < DateTime.Now)
.GroupBy(wa => wa.SkillEN)
.Select(g => new
{
label = g.Key,
count = g.Count()
})
.GroupBy(g => new {g.label, g.count})
.Select(gg => new
{
label = gg.Key.label,
count = gg.Key.count,
pct = gg.Sum(a => a.count)
})
(The dates in the where clause are slightly different, but I don't think it's relevant)
So, how would I implement the over () feature in LINQ to SQL?

Linq - Grouping by date and selecting count

I am currently working through a problem where I would like to run a query which groups the results by the date selected.
For this example, imagine a simple model like so:
public class User
{
public DateTime LastLogIn {get; set;}
public string Name {get; set;}
}
The solution I am looking for is to get a count of Users logged in by Date.
In the database the DateTime are stored with both date and time components, but for this query I really only care about the date.
What I currently have is this:
context.Users
.Where((x.LastLogIn >= lastWeek)
&& (x.LastLogIn <= DateTime.Now))
.GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)EntityFunctions.TruncateTime(x.Key)
}).ToList();
The above however returns an empty list.
End goal is to have a List of objects, which contain a Value (the count of users logged in on a day) and a Day (the day in question)
Any thoughts?
Upon changing the query to:
context.Users
.Where((x.LastLogIn >= lastWeek)
&& (x.LastLogIn <= DateTime.Now))
.GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)x.Key
}).ToList();
it now returns a list with a single item, with the Value being the total count of Users that match the where clause, and the Day being the very first day. It still hasn't seemed to be able to group by the days
NOTE: turns out the above code is right, I was just doing something else wrong.
Sql that it is generating is (note might be very slight syntactical errors here with me adjusting it for the example):
SELECT
1 AS [C1],
[GroupBy1].[A1] AS [C2],
CAST( [GroupBy1].[K1] AS datetime2) AS [C3]
FROM ( SELECT
[Filter1].[K1] AS [K1],
COUNT([Filter1].[A1]) AS [A1]
FROM ( SELECT
convert (datetime2, convert(varchar(255), [Extent1].[LastLogIn], 102) , 102) AS [K1],
1 AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (([Extent1].[LastLogIn] >= #p__linq__1) AND ([Extent1].[LastLogIn] <= #p__linq__2)
) AS [Filter1]
GROUP BY [K1]
) AS [GroupBy1]
You do not need the second TruncateTime in there:
context.Users
.Where((x.LastLogIn >= lastWeek) && (x.LastLogIn <= DateTime.Now))
.GroupBy(x => DbFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
// Replace the commented line
//Day = (DateTime)DbFunctions.TruncateTime(x.Key)
// ...with this line
Day = (DateTime)x.Key
}).ToList();
The GroupBy has truncated the time from the DateTime already, so you do not need to call it again.
To use DbFunctions.TruncateTime you'll need to reference the assembly System.Data.Entity and include using System.Data.Entity;
Note: Edited to address deprecation of EntityFunctions.
Try this:
.GroupBy(x => new {Year = x.LastLogIn.Year, Month = x.LastLogIn.Month, Day = x.LastLogIn.Day)
.Select(x => new
{
Value = x.Count(),
Year = x.Key.Year,
Month = x.Key.Month,
Day = x.Key.Day
})
You can do it easily:
yourDateList.GroupBy(i => i.ToString("yyyyMMdd"))
.Select(i => new
{
Date = DateTime.ParseExact(i.Key, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None),
Count = i.Count()
});
I came across this same problem to get a count based on group by a datetime column. This is what i did. Thanks for some of the answers here. I tried the minimal version and I used this code in .netcore solution.
var result = _context.yourdbmodel
.GroupBy(x=>x.yourdatetimecolumn.Value.Date)
.select(x=> new
{
Count = x.Count(),
Date = (DateTime)x.Key // or x.Key.Date (excluding time info) or x.Key.Date.ToString() (give only Date in string format)
})
.ToList();
Sample output:
[ {
"requestDate": "2020-04-01",
"requestCount": 3 }, {
"requestDate": "2020-04-07",
"requestCount": 14 } ]
Hope this helps someone in future.
You can also do it in one line.
var b = (from a in ctx.Candidates select a)
.GroupBy(x => x.CreatedTimestamp.Date).Select(g => new { Date=(g.Key).ToShortDateString(), Count = g.Count() });
Just convert first IQueryable to IEnumerable(List) with the help of ToList() then use groupby
context.Users
.Where((x.LastLogIn >= lastWeek) && (x.LastLogIn <= DateTime.Now))
.ToList()
.GroupBy(x => x.LastLogIn.Date))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)x.Key
}).ToList();

Categories

Resources