I have two bits of code doing basically the same thing but the results are different. I am trying to get the index number of a record from an ordered list in the database, and then skip/take some results.
Linq method:
int index = db.users.OrderByDescending(o => o.TotalScore)
.ToList()
.FindIndex(f => f.UserId == User.UserId);
// returns 557
SQL method:
WITH IndexedUsers AS (
SELECT UserId,
ROW_NUMBER() OVER (ORDER BY TotalScore desc) AS 'RowNumber'
FROM [User]
)
SELECT RowNumber
FROM IndexedUsers
WHERE UserId = #userId
//Returns 559
Stumped at the difference here. I dont want to be calling ToList on the user collection from the database, i am just using it for testing purposes. I would expect the results to be 1 out with the FindIndex method being 0 based. Any ideas why the values aren't the same?
Related
I'm trying to write a query that returns only those rows that contain the latest date for each name.
So for example, this data:
Name
Date Sold
More Columns...
Bob
2021-01-05
Mike
2021-01-18
Susan
2021-01-23
Bob
2021-02-04
Susan
2021-02-16
Mike
2021-03-02
Would produce this result:
Name
Date Sold
More Columns...
Bob
2021-02-04
Susan
2021-02-16
Mike
2021-03-02
It's sort of like a GROUP BY, but I'm not aggregating anything. I only want to filter the original rows.
How could I write such a query?
NOTE: In the end, this will be a SQL Server query but I need to write it using Entity Framework.
UPDATE: In reality, this is part of a much more complex query. It would be extremely difficult for me to implement this as a raw SQL query. If at all possible, I need to implement using Entity Framework.
Two options
Select top 1 with ties *
From YourTable
Order by row_number() over (partition by Name order by Sold_Date desc)
or slightly more performant
with cte as (
Select *
,RN = row_number() over (partition by Name order by Sold_Date desc)
From YourTable
)
Select *
From cte
Where RN=1
Adapted from
Error while flattening the IQueryable<T> after GroupBy()
var names = _context.Items.Select(row => row.Name).Distinct();
var items =
from name in names
from item in _context.Items
.Where(row => row.Name == name)
.OrderByDescending(row => row.DateSold)
.Take(1)
select item;
var results = items.ToArrayAsync();
Let's break this down:
A query expression which establishes the keys for our next query. Will eventually be run as a subquery.
var names = _context.Items.Select(row => row.Name).Distinct();
Another query, starting with the keys...
var items =
from name in names
... and for each key, let's find the matching row ...
from item in _context.Items
.Where(row => row.Name == name)
.OrderByDescending(row => row.DateSold)
.Take(1)
... and we want that row.
select item;
Run the combined query.
var results = items.ToArrayAsync();
try this
;with Groups as
(
Select [Name], max([Date Sold]) as [Date Sold]
From Table
Group By [Name]
)
Select Table.* From Groups
Inner Join Table on Table.[Name] = Groups.Name And Table.[Date Sold] = Groups.[Date Sold]
I have the following database
my StudentRecords for specific sutdent is
How can i get the latest 3 studentRecords based on the latest Records.RecordDate
so for each sutdent, row will have ReportItems.ReportItemName ReportItems.TotalPSR,, Students.FullName, + add the following columns:
The latest studentRecords based on RecordDate and display the psr + reordsNames + recordDate so that one row look like this
Is this even possible?
And how can i do this using EF6 also what will be the SQL select statment
Providing your EF entities are set up with their mappings correctly to associate the Students, StudentRecords, and Records then you should be able to accomplish this in EF using something like the following:
var results = testContext.Students
.SelectMany(s => s.StudentRecords.OrderByDescending(sr => sr.Record.RecordDate).Take(3))
.GroupBy(x => x.Student)
.ToList();
What this will give you is a result per-student in a grouped structure where the grouping is on the student, with the grouped values are StudentResults. From there you can extract the student details from the "Key", while each result is a collection of (up to) 3 latest StudentRecords in descending order. You will need to handle the scenario where the # of results is < 3.
As a simple example extraction...
foreach (var studentGrouping in results)
{
studentName = studentGrouping.Key.FullName;
psr1 = studentGrouping[0].PSR;
recordName1 = studentGrouping[0].Record.RecordName;
recordDate1 = studentGrouping[0].Record.RecordDate;
// ... etc.
}
Now a caveat to the above solution is that it will invoke lazy-loading on the Records, so up to 3 hits per student. Curiously I tried to mitigate this with .Include(s=>s.StudentRecords.Select(r=>r.Record)) or trying the .Include(r=> r.Record) after the SelectMany() but while the initial query looked to include the columns for the Records, it still triggered the lazy loads.
If anyone can offer a correction to address the lazy loads, I'd love to hear it. I was a bit surprised to see them after adding the Include statements.
If your object model is more complex, such as lots of fields in Student/Record, etc. then I would recommend altering the above to return an anonymous type structure to retrieve just the values you're going to need to populate your results. The above is Ok for simple structures.
For example if Student had 30 columns, but we only care about the ID and Name:
var results = testContext.Students
.SelectMany(s => s.StudentRecords.OrderByDescending(sr => sr.Record.RecordDate).Take(3))
.Select( sr=> new {sr.Student.StudentId, sr.Student.FullName, sr.PSR, sr.Record.RecordId, RecordName = sr.Record.Name})
.GroupBy(x => new{StudentId, FullName})
.ToList();
This would return a structure where the Key was an anonymous type containing just the StudentId and Name, with the grouped values being the type containing the record details for that student. This has the benefit of only returning the data you will need in a single query to the database.
You can use Row_Number and Pivot as below:
;With Cte as (
Select RI.ReportItemName, RI.TotalPSR, S.FullName, SR.PSR, R.[Name], R.[Date],
RowNPSR = Row_Number() over(Partition by SR.StudentRecord order by R.RecordDate DESC),
RowName = Row_Number() over(Partition by SR.StudentRecord order by R.RecordDate DESC) + 500,
RowDate = Row_Number() over(Partition by SR.StudentRecord order by R.RecordDate DESC) + 1000
from
StudentRecords SR
Join Students s
on SR.StudentId = S.StudentId
Join Records R
On SR.RecordId = R.recordId
Left Join ReportItems RI
On S.ReportItemId = RI.ReportItemId
)
Select a.*, p1.[1] as PSR1, p1.[2] as PSR2, p1.[3] as PSR3
, p2.[501] as Name1, p2.[502] as Name2, p2.[503] as Name3 from (
Select * from CTE RowNPSR <= 3 ) a
Pivot (max(PSR) for RowNPSR in ([1],[2],[3]) ) p1
Pivot (max([Name]) for RowName in ([501],[502],[503]) ) p2
Pivot (max([Date]) for RowDate in ([1001],[1002],[1003]) ) p3
I need an EF query to get the row number of a specific entity.
I've looked at this, and this.
Currently I have it working in this way:
private DbContext Context;
public int GetRowNumberQuery<TEntity>(int entityId)
{
var allEntities = this.Context.Set<TEntity>().ToList();
return allEntities
.Select((entity, index) => new { Index = index, Entity = entity })
.Where(x => x.Entity.Id == entityId)
.Select(x => x.Index)
.SingleOrDefault();
}
Obviously, this is very inefficient as it gets a list of all entities before selecting the index. If I remove the .ToList() in the first line, making the whole thing a LINQ query, it fails at the first Select with NotSupportedException saying:
LINQ to Entities does not recognize the method
'System.Linq.IQueryable1[<>f__AnonymousType12[System.Int32,MyEntityType]]
Select[MyEntityType,<>f__AnonymousType12](System.Linq.IQueryable1[MyEntityType],
System.Linq.Expressions.Expression1[System.Func3[MyEntityType,System.Int32,<>f__AnonymousType1`2[System.Int32,MyEntityType]]])'
method, and this method cannot be translated into a store expression.
Can you please tell me how to get the ROW_NUMBER of a specific entity?
Or is it impossible like this pretty old question suggests?
A) Entity Framework doesn't support ROW_NUMBER() (the examples given use EF to generate a query and then "attach" a number to each returned row client side, starting from 1 and going to n if there are n rows)
B) Even in TSQL the query would be complex:
SELECT TOP 1 ROW_NUMBER() OVER (ORDER BY ID) RN FROM SomeTable WHERE ID = 100
would return NULL if there are no rows or 1 if there is a row with ID 100
You would need something like
SELECT B.RN
FROM
(SELECT ID, ROW_NUMBER() OVER (ORDER BY ID) RN
FROM SomeTable) B
WHERE B.ID = 100
C) Clearly you can create a view/stored procedure that uses ROW_NUMBERand call it from EF
I'm struggling trying to generate this LINQ function in a correct T-SQL function.
Please check the following sentence:
// determine the max count of exams applied by students
IQueryable query = (from at in Database.Current.AnsweredTests
where at.TestId == id
group at by at.StudentId into s
select s.Count()).Max();
As you can see this function is wrong talking about syntactically, because Max extension returns int. So which I'm trying to accomplish is to generate a correct T-SQL.
Something like this:
MAX(SELECT x.COUNT()
FROM...
GROUP BY StudentId)
I just did this because I want a good performance, and that is performing a low performance. So my problem is how can I write a correct LINQ sentence with the aggregate functions like MAX and COUNT.
UPDATE:
SELECT [GroupBy1].[A1] AS [C1]
FROM ( SELECT
[Extent1].[StudentId] AS [K1],
COUNT(1) AS [A1]
FROM [dbo].[AnsweredTests] AS [Extent1]
WHERE CAST( [Extent1].[TestId] AS int) = #p__linq__0
GROUP BY [Extent1].[StudentId]
) AS [GroupBy1]
This is what generate the IQueryable (if I remove the max extension, of course). I would like to know if is there a way to include the aggregate function MAX inside of that T-SQL Query to improve the performance on the Server side.
You could also word your query in the following way:
SELECT TOP 1 COUNT(*)
FROM AnsweredTests
WHERE TestId = #id
GROUP BY StudentId
ORDER BY COUNT(*) DESC
Following that logic, this (untested) should be what you are looking for:
var result = (from at in Database.Current.AnsweredTests
where at.TestId == id
group at by at.StudentId into s
orderby s.Count() descending
select s.Count()).First()
You can do ORDER BY DESCENDING and then take first:
var Max = (from at in Database.Current.AnsweredTests
where at.TestId == id
group at by at.StudentId into s
select new { Count = s.Count() }).OrderByDescending(o=>o.Count).First();
Anybody know how to write a LINQ to SQL statement to return every nth row from a table? I'm needing to get the title of the item at the top of each page in a paged data grid back for fast user scanning. So if i wanted the first record, then every 3rd one after that, from the following names:
Amy, Eric, Jason, Joe, John, Josh, Maribel, Paul, Steve, Tom
I'd get Amy, Joe, Maribel, and Tom.
I suspect this can be done... LINQ to SQL statements already invoke the ROW_NUMBER() SQL function in conjunction with sorting and paging. I just don't know how to get back every nth item. The SQL Statement would be something like WHERE ROW_NUMBER MOD 3 = 0, but I don't know the LINQ statement to use to get the right SQL.
Sometimes, TSQL is the way to go. I would use ExecuteQuery<T> here:
var data = db.ExecuteQuery<SomeObjectType>(#"
SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS [__row]
FROM [YourTable]) x WHERE (x.__row % 25) = 1");
You could also swap out the n:
var data = db.ExecuteQuery<SomeObjectType>(#"
DECLARE #n int = 2
SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS [__row]
FROM [YourTable]) x WHERE (x.__row % #n) = 1", n);
Once upon a time, there was no such thing as Row_Number, and yet such queries were possible. Behold!
var query =
from c in db.Customers
let i = (
from c2 in db.Customers
where c2.ID < c.ID
select c2).Count()
where i%3 == 0
select c;
This generates the following Sql
SELECT [t2].[ID], [t2]. --(more fields)
FROM (
SELECT [t0].[ID], [t0]. --(more fields)
(
SELECT COUNT(*)
FROM [dbo].[Customer] AS [t1]
WHERE [t1].[ID] < [t0].[ID]
) AS [value]
FROM [dbo].[Customer] AS [t0]
) AS [t2]
WHERE ([t2].[value] % #p0) = #p1
Here's an option that works, but it might be worth checking that it doesn't have any performance issues in practice:
var nth = 3;
var ids = Table
.Select(x => x.Id)
.ToArray()
.Where((x, n) => n % nth == 0)
.ToArray();
var nthRecords = Table
.Where(x => ids.Contains(x.Id));
Just googling around a bit I haven't found (or experienced) an option for Linq to SQL to directly support this.
The only option I can offer is that you write a stored procedure with the appropriate SQL query written out and then calling the sproc via Linq to SQL. Not the best solution, especially if you have any kind of complex filtering going on.
There really doesn't seem to be an easy way to do this:
How do I add ROW_NUMBER to a LINQ query or Entity?
How to find the ROW_NUMBER() of a row with Linq to SQL
But there's always:
peopleToFilter.AsEnumerable().Where((x,i) => i % AmountToSkipBy == 0)
NOTE: This still doesn't execute on the database side of things!
This will do the trick, but it isn't the most efficient query in the world:
var count = query.Count();
var pageSize = 10;
var pageTops = query.Take(1);
for(int i = pageSize; i < count; i += pageSize)
{
pageTops = pageTops.Concat(query.Skip(i - (i % pageSize)).Take(1));
}
return pageTops;
It dynamically constructs a query to pull the (nth, 2*nth, 3*nth, etc) value from the given query. If you use this technique, you'll probably want to create a limit of maybe ten or twenty names, similar to how Google results page (1-10, and Next), in order to avoid getting an expression so large the database refuses to attempt to parse it.
If you need better performance, you'll probably have to use a stored procedure or a view to represent your query, and include the row number as part of the stored proc results or the view's fields.