converting SQL Group By to EF LINQ Lambda - c#

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

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

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.

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.

Convert SQL statement to Linq / Lambda Query

Im using MEF and executing a task which needs to be grouped with aggregate functions only when it returns more than 1 record. I need both the Max of the start hour and the min of the end hour grouped into a single record like my sql would result in on the restuled task
var ohs = await Bl.UoW.Repositories.OperatingHours
.FindInDataSourceAsync(oh => ((oh.ProductId == productTypeId
&& oh.StateId == state)
|| (oh.StateId == complianceHours.State)));
Here is the SQL that gets me basiccally what I need when more than 1 record returned
SELECT
StateId,
MAX(ComplianceHourStart),
MIN(ComplianceHourEnd)
FROM
OperatingHours
GROUP BY
StateId
HAVING
StateId = 'CA'
So when more than 1 I can filter it further but not sure how to achieve max and min?
if (ohs != null && ohs.Count() > 1)
{
//
ohs = ohs.GroupBy(x => x.State).Max(x => x.ComplianceHourStart?...
}
Thanks
From your SQL, this should be close:
var result = context.OperatingHours
.GroupBy(oh => oh.StateId)
.Select(oh => new {StateId = oh.Key,
MaxStart = oh.Max(x => x.ComplianceHourStart),
MinEnd = oh.Min(x => x.ComplianceHourEnd)});
...although I'm not sure why you are grouping when you are restricting the state id column (group key). The following should also suffice:
var result = context.OperatingHours
.Where(oh => oh.StateId == 'CA')
.Select(oh => new {MaxStart = oh.Max(x => x.ComplianceHourStart),
MinEnd = oh.Min(x => x.ComplianceHourEnd)});
Something like this should do it:
ohs = ohs.GroupBy(x => x.State)
.Select(g => new
{
//You need to make a choice on StateId, here... First one?
StateId = g.First().StateId,
MaxComplianceHourStart = g.Max(o => o.ComplianceHourStart),
MinComplianceHourEnd = g.Min(o => o.ComplianceHourEnd)
});

C# - LINQ query returns null to table and throws exception

I have a piece of code:
var tblGroupedMultiPassive = dtCSV.AsEnumerable()
.Where(r => r.Field<String>("course_type_id") == "3")
.GroupBy(r => new
{
product_id = r.Field<String>("product_id"),
owner_org_id = r.Field<String>("owner_org_id"),
});
if (tblGroupedMultiPassive.Count() > 0)
dtCSVMultiSCOPassive = tblGroupedMultiPassive.Where(grp => grp.Count() > 1)
.SelectMany(grp => grp)
.CopyToDataTable();
Basically on the final statement where assigning to dtCSVMultiSCOPassive, it throws an exception for there being no rows. I know there are rows before this query so it's got to be the LINQ query itself eliminating all of the rows. This is fine, but I need to be able to deal with this situation without it becoming an exception. Any ideas?
You might need to break this into two statements:
DataTable dtCSVMultiSCOPassive = new DataTable();
var query = tblGroupedMultiPassive.Where(grp => grp.Count() > 1).SelectMany(grp => grp);
if(query.Any())
{
dtCSVMultiSCOPassive = query.CopyToDataTable();
}
I would appear that grp.Count() is always 1, which would indicate that you have unique combinations of product_id and owner_org_id in your first query.
BTW, I believe that .SelectMany(grp => grp) is completely redundant.
Are you sure that this statement doesn't return null:
dtCSVMultiSCOPassive = tblGroupedMultiPassive.Where(grp => grp.Count() > 1);
?
So because of the first conditional tblGroupedMultiPassive.Count() > 0 seems to be true I would try:
if (tblGroupedMultiPassive.Count() > 0)
{
dtCSVMultiSCOPassive = tblGroupedMultiPassive.Where(grp => grp.Count() > 1);
if(dtCSVMultiSCOPassive != null)
dtCSVMultiSCOPassive = dtCSVMultiSCOPassive.CopyToDataTable();
}
Infact the problem could be that almost every grp contains only one element so the query returns null becuase of the second conditional grp.Count() > 1 in your query.

Categories

Resources