Grouping by day in LINQ without TruncateTime() - c#

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.

Related

C# db query where conditions are met, orderby date and then get the first result

While evaluating some queries we found some possible optimization. The ideia is shown below but I currently don't know how to solve this.
Current query:
public static List<Object> SampleQuerySales(int store_id)
{
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id))
select new Object {
...
}).ToList();
return query;
}
This returns all sales made, but its required only the latest sale (OperationDate) from a datetime reference. As obvious this became a bottleneck.
My ideia was to make it similar to query below, which is incorrect (doesn't compile). How can I achieve this dataset?
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate).FirstOrDefault() //error
select new Object {
...
}).Tolist();
Since you only want one value from table2, use let instead of from:
var query = (from client in db.table1.Where(p => p.store_id == store_id)
let mostRecentSaleAfterDateReference = db.table2
.Where(q => q.customer_id == client.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate)
.FirstOrDefault()
select new Object {
...
}).Tolist();

How to make an Entity Framework query with a sum of the price that depends of the date?

Hello StackOverflow community,
I have a question,
I want to create a query which is able to do the some of all price (which is a double), depends of the date.
--SQL Query
select sum(price) from dbo.contracts where create_date ='2009-01-01' group by (id);
I tried this Linq query but it didn't worked :
--Linq Query
public static double getPriceFromMonth(int year, int month)
{
return unitOfWork.Contracts.GetAll().Where(c => c.create_date.Year == year && c.create_date.Month == month).GroupBy(c => c.id).Select(s => new { total = s.Sum(c => c.price) });
}
I hope this isn't too complicated ^^'
Try:
unitOfWork.Contracts.GetAll()
.Where(c => c.create_date.Year == year && c.create_date.Month == month)
.Sum(c => c.price);

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()?

Using linq to find MIN(date) within linq query

I am using linq to extract data. This data contains a date and some other values. The thing i that these dates can occur more then once, because the dates can have the same value but a different timestamp. I want to extract the anonymous type with the earliest timestamp. How can i do this in linq ?
this is my code:
var result = (from a in UnitOfWork.ActivityLessonParticipantService.Query
.Where(a => a.ActivityLesson.Activity.Id == activityId)
.Where(a => a.ActivityLesson.From >= startDate && (a.ActivityLesson.To == startDate || a.ActivityLesson.To <= endDate)).OrderBy(d => d.ActivityLesson.From)
where !a.ActivityLesson.IsDeleted && !a.ActivityLesson.Activity.IsDeleted && a.Appeared == true
select new
{
CPR = a.User.UserName,
FullName = a.User.FullName,
ActivityFromDate = a.ActivityLesson.From,
}).OrderBy(c => c.CPR).ToList();
thanks
You can GroupBy the Date property of DateTime and then order this group by DateTime and use First to pick only the first record/object:
var query = from a in UnitOfWork.ActivityLessonParticipantService.Query
where a.ActivityLesson.Activity.Id == activityId
&& a.ActivityLesson.From >= startDate
&& (a.ActivityLesson.To == startDate || a.ActivityLesson.To <= endDate)
&& !a.ActivityLesson.IsDeleted
&& !a.ActivityLesson.Activity.IsDeleted
&& a.Appeared
select a;
var firstByDate = query.GroupBy(a => a.ActivityLesson.From.Date)
.Select(grp => grp.OrderBy(a => a.ActivityLesson.From).First())
.OrderBy(a => a.User.UserName)
.Select(a => new
{
CPR = a.User.UserName,
FullName = a.User.FullName,
ActivityFromDate = a.ActivityLesson.From,
}).ToList();
Due to LINQ's deferred execution this is actually a single query that gets executed at the final ToList. I'm mixing query and method syntax because i prefer method syntax when it comes to GroupBy but it's a matter of taste.

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