Slow LINQ Query - linq

I have a query that's running slow (in a loop of about 100 it takes 5-10 seconds) and have no clue why. It's simply querying against a List of objects... your help is much appreciated!
I'm basically querying for Schedules that have been assigned to specific managers. It must be from the specified Shifts week OR the first 2 days of next week OR the last 2 days of the previous week.
I tried calculating .AddDays before but that didn't help. When I ran a performance test it highlighted the "from" statement below.
List<Schedule> _schedule = Schedule.GetAll();
List<Shift> _shifts = Shift.GetAll();
// Then later...
List<Schedule> filteredSchedule = (from sch in _schedule
from s in _shifts
where
**sch.ShiftID == s.ShiftID
& (sch.ManagerID == 1 | sch.ManagerID == 2 | sch.ManagerID == 3)
& ((s.ScheduleWeek == shift.ScheduleWeek)
| (s.ScheduleWeek == shift.ScheduleWeek.AddDays(7)
& (s.DayOfWeek == 1 | s.Code == 2))
| (sch.ScheduleWeek == shift.ScheduleWeek.AddDays(-7)
& (s.DayOfWeek == 5 | s.Code == 6)))**
select sch)
.OrderBy(sch => sch.ScheduleWeek)
.ThenBy(sch => sch.DayOfWeek)
.ToList();

First port of call: use && instead of & and || instead of |. Otherwise all the subexpressions in the where clause will be evaluated, even if the answer is already known.
Second port of call: use a join instead of two "from" clauses with a where:
var filteredSchedule = (from sch in _schedule
join s in _shifts on s.ShiftID equals sch.ShiftID
where ... rest of the condition ...
Basically that's going to create a hash of all the shift IDs, so it can quickly look up possible matches for each schedule.

Related

How to make efficient search with LINQ in MVC4? [duplicate]

This question already has answers here:
entity framework: conditional filter
(3 answers)
Closed 6 years ago.
Hi I am developing MVC4 application. I am developing one search page which contains 5 textboxes and search button in which only one textbox is mandatory field. Remaining are not mandatory. I want to have AND of all 5 textbox values. My query is as below.
var logDetails = (from c in db.ts_upldlog_content
join tbl in db.ts_upld_doc on c.upld_docid equals tbl.upld_docid
join doc in db.tm_doc_type on tbl.upld_doctypeid equals doc.doc_typeid
where
(tbl.upld_clientid == clientId && tbl.upld_employeeid == employeeID && tbl.upld_empcitizenid == citizenId && tbl.upld_doctypeid == typeofDocument && EntityFunctions.TruncateTime(c.updatedOn) >= start && EntityFunctions.TruncateTime(c.updatedOn) < end)
select new logdetails
{
//Getting all properties
}).toList();
My problem is In the above query start and end are mandatory so i will get some value from UI. Let me explain one scenario. User will supply start and end and remaining will be null. My query will not yield results because I am doing && operation and my other fields may have some value in DB.
Lets conside below table.
clientID EmployeeID EmpCitiID docType Date
123 456 456 1 10/19/2016
When i Pass only Date my query will not work because I am doing && operation. so Is there any way to achieve this scenario? Any help would be appreciated. Thank you.
tbl.upld_clientid == clientId && tbl.upld_employeeid == employeeID && tbl.upld_empcitizenid == citizenId && tbl.upld_doctypeid == typeofDocument &&
(EntityFunctions.TruncateTime(c.updatedOn) >= start && EntityFunctions.TruncateTime(c.updatedOn) < end ) )
missing parenthesis

multiple group by using linq

I need return just 2 lines in my query. One line with a string Today and a number of cases closed today, on my second line I need a string Last Week and a number of cases closed on the last week.
How I group with a range date?
Sum Name
----------- ----------
12 Today
33 Last Weeb
How about this:
var caseCounts = Cases
.Where(c => c.Date == today || (c.Date >= startOfLastWeek && c.Date <= endOfLastWeek))
.GroupBy(c => c.Date == today ? "Today" : "Last Week")
.Select(g => new {
Name = g.Key, Sum = g.Count()
});
You would need to define the 3 dates (today, startOfLastWeek, endOfLastWeek) before hand, but it gives you the results you are after.
GROUP BY YEARWEEK(date) should work. Depending on your dbms, you might be able to use another function, or program your own.
http://www.tutorialspoint.com/sql/sql-date-functions.htm#function_yearweek

Orderby not ordering strings that include numbers in Linq

I have a string field in database that keeps numbers as string. when I want to order based on this field in Linq as below but orderby does not work well. it orders like this
1 - 2 - 3 - 25 - 11 - 30 - 50 ===> 1 - 11 - 2 - 25 - 3 - 30 - 50
IQueryable<Tbl_Melk> Melks =
from melk in Tbl_Melk
where melk.Mantaghe == Mantaghe && melk.Hoze == Hoze && melk.Block == Block
orderby melk.Melk
select melk;
What says Maurice is right, You may try this:
var Melks = from melk in Tbl_Melk.ToList()
let integer = int.Parse(melk)
orderby integer
where melk.Mantaghe == Mantaghe && melk.Hoze == Hoze && melk.Block == Block
orderby melk.Melk
select melk;
But with that you are losing performance and the IQuerable interface.
Actually, that is the correct order because it's sorting the values in alphabetic order. LINQ cannot tell that the data contains numbers unless you coerce it into a numeric type. Check out this question for one way to sort the way you want.
Linq - Order by number then letters
LeftPad the string with zeros.
Out of my brain and to the lack of EntityFramework not supporting LeftPad:
....
orderby SqlFunctions.Replicate("0", 10 - melk.Melk.Length) + melk.Melk

LINQ get Max value from list

I have the following table:
ID Amt Received
-- ---- --------
2 55 N
2 88 Y
2 44 N
3 5 N
3 9 N
4 5 N
5 33 Y
6 43 N
7 54 N
var result = (from rs in db.Exp
where rs.ID == id
&& rs.Received == true
select rs).Max().Any();
Given an ID, I need to find the max Amt for a given id and then check if it is Y, if so, return true else return false.
This should do it;
db.Exp.
Where(x => x.ID == id).
OrderByDescending(x => x.Amt).
Take(1).
Any(x => x.Received == "Y");
Unfortunately LINQ doesn't provide a "max by an attribute" method. MoreLINQ does with its MaxBy operator, but that can't be translated into SQL of course. So if this is a LINQ to SQL (or whatever) query, you'll need a different approach. If it's already LINQ to Objects, however:
return db.Exp.Where(rs => rs.ID == id)
.MaxBy(rs => rs.Amt)
.Received;
Note that this is doing what the words of your question ask:
Out of the records with the given ID...
Find the one with the highest amount...
And check the value of Received
This is not the same as:
Out of the records with the given ID where received is true...
Find the one with the highest amount
Also note that this will throw an exception if there are no records with that ID.
If you want to do it in LINQ to SQL etc, you'd probably be best off with an ordering:
var highest = db.Exp.Where(rs => rs.ID == id)
.OrderByDescending(rs => rs.Amt)
.FirstOrDefault();
return highest != null && highest.Received;
You don't want to do this if you're using LINQ to Objects, as it will order all the results, when you only want to find the result with the highest amount.
You need to tell it what you want the Max of.
var result =
(from rs in db.Exp
where rs.ID == id && rs.Received
select rs)
.Max(row => row.Amt) == Y;
And you don't need the .Any() at all
// "I need to find the max Amt for a given id..."
var elementWithMaxAmount =
db.Exp
.Where(x => x.ID == id)
.OrderByDescending(x => x.Amount)
.FirstOrDefault();
// "... and then check if it is Y"
var result = (elementWithMaxAmount != null &&
elementWithMaxAmount.Received == "Y");

How to use LINQ To Entities for filtering when many methods are not supported?

I have a table in SQL database:
ID Data Value
1 1 0.1
1 2 0.4
2 10 0.3
2 11 0.2
3 10 0.5
3 11 0.6
For each unique value in Data, I want to filter out the row with the largest ID. For example: In the table above, I want to filter out the third and fourth row because the fifth and sixth rows have the same Data values but their IDs (3) are larger (2 in the third and fourth row).
I tried this in Linq to Entities:
IQueryable<DerivedRate> test = ObjectContext.DerivedRates.OrderBy(d => d.Data).ThenBy(d => d.ID).SkipWhile((d, index) => (index == size - 1) || (d.ID != ObjectContext.DerivedRates.ElementAt(index + 1).ID));
Basically, I am sorting the list and removing the duplicates by checking if the next element has an identical ID.
However, this doesn't work because SkipWhile(index) and ElementAt(index) aren't supported in Linq to Entities. I don't want to pull the entire gigantic table into an array before sorting it. Is there a way?
You can use the GroupBy and Max function for that.
IQueryable<DerivedRate> test = (from d in ObjectContext.DerivedRates
let grouped = ObjectContext.DerivedRates.GroupBy(dr => dr.Data).First()
where d.Data == grouped.Key && d.ID == grouped.Max(dg => dg.ID)
orderby d.Data
select d);
Femaref's solution is interesting, unfortunately, it doesn't work because an exception is thrown whenever "ObjectContext.DerivedRates.GroupBy(dr => dr.Data).First()" is executed.
His idea has inspired me for another solution, something like this:
var query = from d in ObjectContext.ProviderRates
where d.ValueDate == valueDate && d.RevisionID <= valueDateRevision.RevisionID
group d by d.RateDefID into g
select g.OrderByDescending(dd => dd.RevisionID).FirstOrDefault();
Now this works.

Resources