EF Core Groupby Get highest value of row - c#

I have the following table:
| Id | ParentId | Version |
---------------------------
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 4 | 0 |
| 5 | 4 | 1 |
| 6 | 4 | 2 |
I am looking for a query (pref Linq to EF) where I can select the highest Version per group of ParentIds.
I currently have something like this:
await _context.Actions.GroupBy(a => a.ParentId)
.Select(s => s.Max(a => a.Version))
.ToListAsync();
The only problem I have is that only the version row is returned but I'd like the whole row to be returned per group.

You would need to order by descending and take the first in each group
await _context.Actions
.GroupBy(a => a.ParentId)
.Select(s => s.OrderByDescending(a => a.Version).First())
.ToListAsync();

Related

Entity Framework: Get the last message from each conversation?

My table Chats
id | sender | receiver | message | date
1 | 1 | 2 | Hello | 2015-12-08 20:00
2 | 2 | 1 | Hey | 2015-12-08 20:10
3 | 2 | 1 | You there? | 2015-12-08 21:00
4 | 1 | 3 | Yes | 2015-12-08 21:15
5 | 4 | 1 | Hey buddy | 2015-12-08 22:00
My controller
...
[HttpPost]
public async Task<JsonResult> ChatList(int person_id)
{
IEnumerable<Chat> chats = db.Chats.Where(p=>(p.sender==person_id||p.receiver==person_id));
return Json(chats);
}
How do I get the latest message from each conversation?
var result = db.Chats.GroupBy(c => (Math.Min(c.id1, c.id2), Math.Max(c.id1, c.id2)))
.Where(g => g.Key.Item1 == person_id || g.Key.Item2 == person_id)
.Select(g => g.OrderBy(c => c.date))
.Select(g => g.Last().message);
For efficiency you can replace OrderBy with an Aggregate that would fetch the latest element in O(n), but this illustrates the concept I think.

Linq Return List of Objects after max effective From Date

Hiiya,
So trying to work out a query that returns a list of objects based on its max effective from date before a selected datetime.
Principle Example
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 1 | 1 | 10.11 | 20/10/2017 |
+----+--------------+------------+----------------+
| 2 | 1 | 15.11 | 24/10/2017 |
+----+--------------+------------+----------------+
| 3 | 1 | 20.11 | 30/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
| 5 | 2 | 9.89 | 25/10/2017 |
+----+--------------+------------+----------------+
| 6 | 2 | 12.89 | 29/10/2017 |
+----+--------------+------------+----------------+
so say I want to return each employees record as of 21/10/17 I would expect the below to be returned as a list of objects (Entities)
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 1 | 1 | 10.11 | 20/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
Then as of 24/10/2017
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 2 | 1 | 15.11 | 24/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
Im guessing the Query should be something along these lines but cant work out what it should be.
var qry = from t in db.Entity.Where(x => x.Effective_From <= as_of_date)
.OrderBy(x => x.Employee_Num)
select new Entity { *need rest of entity fields* ,effective_from = Max(e => e.Effective_From) };
Any help to finish off or point me in a different direction would be appreciated...
I believe what you'd be looking to do would involve a GroupBy expression to group by employee number, ordered by Employee number and then effective from descending, then take the first available combination.
var qry = db.Entity.Where(x => x.Effective_From <= as_of_date)
.OrderBy(x => x.Employee_Num)
.ThenByDescending(x => X.Effective_From)
.GroupBy(x => x.Employee_Num)
.Select(g => g.FirstOrDefault())
.ToList();
This is if you want the actual entities. I'd avoid doing something like "select new" for an entity as while this will contain the data of the relevant entities, they aren't the same reference as far as the context is concerned. .Select() would be used to retrieve an anonymous type suited for some logic or to populate a view model for a front end, or a DTO for an API result.

Get record for each type with latest / bigger dates in C# DBML

I have a DB table which has some data as follows:
LINE | QTY | USERID | DATE
-----------------------------------------
1 | 5 | qb1 | 2015-03-02 11:23:25
2 | 1 | qb2 | 2015-03-02 18:24:03
3 | 3 | ch1 | 2015-03-03 05:38:49
1 | 2 | qb1 | 2015-03-03 08:47:02
2 | 4 | qb2 | 2015-03-03 14:01:31
3 | 2 | ch1 | 2015-03-03 21:11:53
1 | 4 | qb1 | 2015-03-04 09:34:04
2 | 5 | qb2 | 2015-03-04 15:29:27
3 | 1 | ch1 | 2015-03-04 19:28:33
As you can see I have only 3 unique LINE values in the DB. I require a LINQ query to select the latest record of every line. The date can be any date, I just need the latest status of the lines based on "DATE" field.
At the moment I am doing it very roughly something like this:
var line1 = db.GetTable<lnk_sts>().Where(x=> x.LINE== 1).OrderByDescending(x => x.DATE).FirstOrDefault();
Same for the other 2. What I Require is a list of lnk_sts with only the ones with a bigger date, in this case:
LINE | QTY | USERID | DATE
---------------------------------------
1 | 4 | qb1 | 2015-03-04 09:34:04
2 | 5 | qb2 | 2015-03-04 15:29:27
3 | 1 | ch1 | 2015-03-04 19:28:33
What you need to do is, group on Line and then take the first item in group after ordering in descending.
db.GetTable<lnk_sts>()
.GroupBy(x=>x.LINE)
.Select(x=>x.OrderByDescending(o=>o.DATE).FirstOrDefault())
.ToList();
First group by the line, then order by date and take the last item.
var result = db.GetTable<lnk_sts>().GroupBy(x=> x.LINE).Select(x => x.OrderBy(y => y.Date).Last())

linq query to select top 3 from a group and get the average of those selected

I am struggling with a linq query.
I would like to:
Select for each schedule_Region_ids the bins that do not fail qc (QC=1)
Select the top 3 bins that do not have QC fail mark (1) (for example, bins 3 4 and 5 if no QC mark =1)
Average the 3 selected values to create 'BaselPeak' for each schedule_Region_id.
I have tried the following code:
var categories = drugonboard
.Where(p => p.Field<int?>("QC") != 1)
.OrderByDescending(x => x.Field<int>("bin")).Take(3)
.GroupBy(t => new { ID = t.Field<int>("schedule_region_id")})
.Select(g => new
{
schedule_region_id = g.Key.ID,
BaselPeak = g.Average(p => p.Field<double>("AnalytePeak"))
});
However I end up with the top 3 schedule_region_ids and not the average of the top three bins for each schedule_region_id. If i try to GroupBy first then i get anonomous type errors.
I have a dataset which looks like this:
id | bin | AnalytePeak | QC
1 | 1 | 620 |
1 | 2 | 1020 |
1 | 3 | 681 | 1
1 | 4 | 1190 |
1 | 5 | 1200 |
---------------------------
2 | 1 | 1020 |
2 | 2 | 1076 |
2 | 3 | 1190 |
2 | 4 | 1200 |
2 | 5 | 358 | 1
---------------------------
3 | 1 | 1020 |
3 | 2 | 1076 |
3 | 3 | 1190 |
3 | 4 | 1200 |
3 | 5 | 1358 |
You need to take 3 after grouping and after sorting the child records.
var categories = drugonboard
.Where(p => p.Field<int?>("QC") != 1)
.GroupBy(t => new { ID = t.Field<int>("schedule_region_id")})
.Select(g => new {
schedule_region_id = g.Key.ID,
BaselPeak = g.OrderByDescending(x => x.Field<int>("bin"))
.Take(3)
.Average(p => p.Field<double>("AnalytePeak"))
});
This assumes:
If all bins are QC=1, then you don't want the schedule_region_id parent at all and by top 3, you mean the highest 3 bin numbers.

Recent users query

I have 2 tables, for example let's assume that this is the data:
users table:
------------------------------
| user_id | username |
------------------------------
| 1 | danny |
| 2 | george |
| 3 | lana |
| 4 | kim |
| 5 | tim |
------------------------------
users_logins table:
-----------------------------------------------
| record_id | user_id | recorder |
-----------------------------------------------
| 1 | 3 | 2012-11-06 04:18:26 |
| 2 | 3 | 2012-11-06 04:31:05 |
| 3 | 2 | 2012-11-06 03:44:22 |
| 4 | 1 | 2012-11-06 04:18:58 |
| 5 | 1 | 2012-11-06 04:30:15 |
| 6 | 3 | 2012-11-06 04:31:05 | X
| 7 | 1 | 2012-11-06 05:53:47 |
| 8 | 1 | 2012-11-06 05:55:15 | X
| 9 | 4 | 2012-11-06 05:59:31 |
| 10 | 4 | 2012-11-06 06:12:55 | X
-----------------------------------------------
I want to show 3 recent logged in users, so the result will show only the rows that marked with X right to them, or in words unique recent logins.
How would the query look like?
Try this for linq to objects
(from login in users_logins.OrderByDescending(user => user.recorder)
from user in users
where user.user_id == login.user_id
select user).Distinct().Take(3)
This query
1. first sorts on date
2. then joins sorted login data with user data,
3. then take distinct users
4. and then finally take first 3 records.
Following is alternative query
from login in users_logins.OrderByDescending(user =>user.recorder).GroupBy(user=>user.recorder).SelectMany( users=>users.First()).Take(3)
from user in users
where user.user_id == login.user_id
select user
This query
1. first sorts
2. then groups on user_id
3. then take first 3 records with different user id,
4. then joins with users data.
I think this is what you want.
var resentLogedInUsers =
users
.Join(
users_logins,
u => u.user_id,
l => l.user_id,
(u, l) => new
{
User = u,
Login = l.recorder
})
.GroupBy(j => j.User)
.Select(l => new {User = l.Key, LastLogin = l.Max(d => d.Login)})
.OrderByDescending(r => r.LastLogin)
.Take(3);

Categories