Get only rows with the latest date for each name - c#

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]

Related

string.Join in Entity framwork, LINQ to SQL. Without Client-side evaluation

If you have a table, similar to here:
DataTypeID, DataValue
1,"Value1"
1,"Value2"
2,"Value3"
3,"Value4"
and want output like this:
DataTypeID,DataValues
1,"Value1,Value2"
2,"Value3"
3,"Value4"
Most questions suggest like this to use toList() or AsEnumerable() and then, string.Join(", ", DataValues) on client-side. This might work if the data is not huge but it defeats the purpose of using EF. How can I do this without loading all the data in-memory?
UPDATE: As of EF7 preview 7, now you simply use string.Join normally for example:
_context.MyTable
.GroupBy(keySelector => keySelector.MyKey, elemSelector => elemSelector.StringProp)
.Select(elem => string.Join(',', elem))
//.FirstOrDefaultAsync(cancellationToken), if (keyselector => 1) i.e. only 1 group so you get all rows
Old answer
Well, as per this this issue, string.Join() is yet to be implemented(as of now) and IEnumerable.Aggregate will not translate either.
In the meanwhile, you can create a view and write your SQL there.
For example, to group by id and string.Join(", ", Names);
CREATE VIEW V_Name AS
SELECT ID,
Names=STUFF
(
(
SELECT DISTINCT ' || '+ CAST(Child.Name AS VARCHAR(MAX))
FROM Child,MainTable
WHERE Main.ID= t1.ID --this line is imp...
AND Child.ID=MainTable.ID
FOR XMl PATH('')
),1,1,''
)
FROM MainTable t1
GROUP BY t1.IDReview
OR
CREATE VIEW V_Name AS
SELECT ID, STRING_AGG(Name, ', ') AS Names
FROM MainTable
LEFT JOIN ChildTable ON MainTable.ID = ChildTable.ID
GROUP BY ID
Now, in your C# you can simply join this with your ID, just like you normally would with an IQueryable:
from data in _dbcontext.sometable
join groupedAndJoinedNames in _dbcontext.viewname
on data.ID equals groupedAndJoinedNames.ID
select new
{
Names = groupedAndJoinedNames.Names
}

Select latest records and add results as new columns

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

Nested Select MySQL statements to LINQ

I'm trying to convert the following MySQL statement in to LINQ query format
SELECT * FROM table1 WHERE table1.id IN (SELECT c_id FROM table2 WHERE a_id IN (1, 49) GROUP BY c_id HAVING COUNT(*) = 2) ORDER BY name
Got as far as this, but I'm drawing a blank on how to handle the IN and 2nd SELECT statement
myItems = from c in table1
let id = c.id
where ????
orderby c.name
select c;
Would appreciate some guidance with this please
Try this:
var ids=new[]{1,49};
var innerquery=table2.Where(e=>ids.Contains(e.a_id))
.GroupBy(e=>e.c_id)
.Where(g=>g.Count()==2)
.Select(g=>g.Key);
var myItems = from c in table1
where innerquery.Contains(c.id)
orderby c.name
select c;
First define your inner query,after the group by you will get a collection of IGrouping<TKey, TElement>> that represent a collection of objects that have a common key, filter the groups choosing only those where count==2, and select the keys of those groups. The second part is really easy to understand. I split the process in two queries to do it more readable, but you can merge both query in one.

How to translate this Queryable linq function

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

Linq query for a nested select statement with grouping and distinct

I'd like to translate the following SQL statement into a linq query:
select COUNT(*), itemid, globalid, title, preview, previewimage, previewimage_alt, link
from (
select distinct Id, itemid, globalid, title, preview, previewimage, previewimage_alt,
(select top 1 link from LikeCounter where GlobalId=x.GlobalId) as link
from [LikeCounter] x
where PortalId=1 and LanguageId=1
) as t
GROUP BY itemid, globalid, title, preview, previewimage, previewimage_alt, link
ORDER BY COUNT(*) desc
The query is over a view that holds records of objects being "liked". Since the objects can be published in multiple places, and the view was setup to allow for filtering for a certain place, it requires a distinct before grouping the records to find out the view count (that's the reason for the additional query for the "link" column).
Is a nested SELECT statement possible in one linq statement?
The inner query is no problem:
(from x in LikeCounter
where x.PortalId==1 && x.LanguageId==1
select new {x.Id, x.ItemId, x.GlobalId, x.LanguageId, x.Title, x.Preview, x.PreviewImage_alt,
Morelink=(from y in LikeCounter
where y.GlobalId==x.GlobalId
select y.Morelink).FirstOrDefault()
}).Distinct()
But is there a way to extend this with the grouping of the distinct records, that results in just one query to the database ?
Thanks in advance for any input...
Nina
Edit:
the following query almost returns what I want -- but produces multiple queries to the SQL server:
(from y in
((from x in LikeCounter
where x.PortalId==1 && x.LanguageId==1
select new {x.Id, x.ItemId, x.GlobalId, x.LanguageId, x.Title, x.Preview, x.PreviewImage_alt,
Link=(from y in Xparo_LikeCounter
where y.GlobalId==x.GlobalId
select y.Link).FirstOrDefault()
}).Distinct())
group y by y.GlobalId into grp
select new {Data=grp, Count= grp.Count()}).OrderByDescending (x => x.Count)
I Think the below should work but i can't really test it. No idea how many queries it would take either
from subq in (from x in LikeCounter
where x.PortalId==1 && x.LanguageId==1
select new {x.Id, x.ItemId, x.GlobalId, x.LanguageId, x.Title, x.Preview, x.PreviewImage_alt,
Morelink=(from y in LikeCounter
where y.GlobalId==x.GlobalId
select y.Morelink).FirstOrDefault()
}).Distinct()
group subq by new {TheCount = subq.Id.Count(), subq.Id, subq.ItemId, subq.GlobalId, subq.LanguageId, subq.Title, subq.Preview, subq.PreviewImage_alt, subq.Morelink } into grouped
order by grouped.TheCount descending;

Categories