LINQ Lambda group by multiple columns following by select multiple columns - c#

Lets assume we have some data in a table:
Id OrderNr Name Date
1 1 John 2011-01-01
2 1 John 2012-01-01
3 2 Paul 2011-02-02
I want to use linq (lambda expressions) to group by OrderNr and Name and select OrderNr, Name and First occuring Date. So the result should be:
OrderNr Name Date
1 John 2011-01-01
2 Paul 2011-02-02
How should this be written?

This should work:
var results1 = (from t in DB.Table
group t by new { t.OrderN, t.Name } into grp
select new
{
OrderN = grp.Key.OrderN,
Name = grp.Key.Name,
FirstDate = grp.FirstOrDefault().Date
}).ToList();

Related

group by linq to entity query to get one record having latest timestamp by joining tables

There are two tables and using linq query to get records. From second table, there can be multiple rows corresponding to first table with date timestamp... based on below query, I am getting all records, but is there a way we can get the row from second table which has latest timestamp ?
Table Parent
ID Name
1 M
2 N
3 O
4 P
5 Q
Table Child
Id fkID DateTime
1 2 01/12/2021 09:12:20
2 2 01/12/2021 09:13:20
3 2 01/12/2021 09:14:20
4 2 01/12/2021 09:15:20
5 2 01/12/2021 **09:16:20**
Linq query:
from p in Parent
join c in Child on p.id equals c.fkId into cJoin
from cJoin in cJoin.DefaultIfEmpty()
select new TempResponse
{
Id = p.Id,
Name = p.Name,
Date = c.Date
}
I am getting 10 records using above query but just need 5 records i.e. from child table instead of all 5 records, we need a record that has latest time stamp
**expected output**
1 M
2 N 01/12/2021 09:16:20
this record is 5'th record from child table because this one has latest date time stamp
( latest record )
3 O
4 P
5 Q
Is there any way we can use group by and get the record that has latest time stamp from second table ?
Assuming you have defined navigation properties for the FK, I would use a query like;
dbContext.Child.Where(c => c.Date == c.Parent.Child.Max(c2 => c2.Date))
I believe you can use:
var ans = from p in Parent
join cmax in (
from c in Child
group c by c.fkId into cg
select cg.OrderByDescending(c => c.Date).FirstOrDefault())
on p.Id equals cmax.fkId into cJoin
from c in cJoin.DefaultIfEmpty()
select new TempResponse {
Id = p.Id,
Name = p.Name,
Date = c != null ? c.Date : null
};
Note that the order of results seems to vary on SQL Server unless you add another orderby clause before the select to force an order.

Not a simple Max

I have the following data:
Id | Value | OtherStuff
---------------------------
6 | 6 | 1
---------------------------
5 | 4 | 2
---------------------------
5 | 2 | 3
The desired result:
Id | Value | OtherStuff
---------------------------
6 | 6 | 1
---------------------------
5 | 4 | 2
That is I need the Max Value for each of the Id's.
I'm a bit stumped of how to do this without breaking it into multiple queries, can it be done, and if so how?
Update: I think I oversimplified the issue:
var query = from st in StockStakes
join o in Organisations on j.OrganisationId equals o.OrganisationId into oGroup
from o in oGroup.DefaultIfEmpty()
where st.Stock.Status == "A"
select new
{
Id = st.Id,
Value = st.Value,
CustomerId = o.OrganisationId
};
The data sample from above still stands... now how do i structure the query to give me the Max Value alongside each Id?
var query = from x in data
group x by x.Id into x
select x.OrderByDescending(y => y.Value).FirstOrDefault()
Based on you updated query, similar approach to the first query, but since you have multiple tables you need to group all the tables into an anonymous object and then select only the columns you want
var query = from st in StockStakes
join o in Organisations on j.OrganisationId equals o.OrganisationId into oGroup
from o in oGroup.DefaultIfEmpty()
where st.Stock.Status == "A"
group new { st, o } by st.Id into g
let largestValue = g.OrderByDescending(x => x.Value).FirstOrDefault()
select new
{
Id = g.Key,
Value = largestValue.st.Value,
CustomerId = largestValue.o.OrganisationId
};
I'm not really sure about what you mean, but maybe you can try with this query.
select Id, max(Value)
from your_table
group by Id;
This gives you the max "Value column" value for each "Id column" value.
-- EDIT --
LINQ version:
var q = from t in dc.YourTable
group t by t.Id
into g
select new
{
Id = g.Id,
Value = (from t2 in g select t2.Value).Max()
};
Code not tested. I'm on the bus now... :-) Give it a try!

LINQ left join ordered with empty right-items at the end

My understanding of LINQ and Entity Framework is minimal, and I'm learning as I go along...
I am trying to write a query that takes the information from a view called GroupView and does a left-join on a table called GroupSequence... the information is then to be used by an <asp:Repeater>.
The resultant set should have all the items from GroupView with the joined items at the start (in sequence defined by the GroupSequence table) and with non-joined items at the end (in sequence defined by the Id of the GroupView items).
I.e...
[GroupView] | [GroupSequence]
[Id] [Name] [Calc] | [Id] [GroupId] [UserId] [Sequence]
1 Group 1 23 | 1 1 1 3
2 Group 2 34 | 2 2 1 2
3 Group 3 45 | 3 3 1 1
4 Group 4 56
5 Group 5 67
With the expected outcome of...
[Id] [Name] [Calc]
3 Group 3 45
2 Group 2 34
1 Group 1 23
4 Group 4 56
5 Group 5 67
If I do the following, despite using the DefaultIfEmpty, all I get is the 3 groups linked to the sequence. But the page displays, even though it's only 3 rows...
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty()
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
If I do the following, the .DataBind on the repeater complains that...
The entity or complex type 'DevModel.GroupSequence' cannot be constructed in a LINQ to Entities query
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
Based on this question and accepted answer I have also tried using a DTO like this...
class GroupViewSequenceDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int? Calc { get; set; }
}
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new GroupViewSequenceDTO { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
But I still get the same error as before (cannot be constructed)
The question...
How can I write this query so that the repeater will show all 5 rows, with the first 3 in sequence order, and the last 2 added on? What am I doing wrong?
You need to move the filter into the left join condition since it will be false when item is null.
from #group in context.GroupViews
from seq in context.GroupSequences.Where(x => #group.Id == x.GroupId && x.UserId == 1).DefaultIfEmpty()
orderby seq.Sequence ?? int.MaxValue
select new GroupViewSequenceDTO
{
Id = #group.Id,
Name = #group.Name,
Calc = #group.Calc
};

Linq to Sql Union statement

I have two resultsets that I'm trying to union on (LINQ to SQL/Entity)
var ldrSet = (from ldr in Leader
join emp in employee
on ldr.ID equals emp.ID
where ldr.ID.Contains("123")
select new {ID = ldr.ID, Name = emp.firstName + " " + emp.lastName, flag = "Edit"});
var allEmpSet = (from emp in employee
where emp.ID.Contains("123")
select new {ID = ldr.ID, Name = emp.firstName + " " + emp.lastName, flag = "New"});
var results = ldrSet.Union(allEmpSet);
When I run this query I get something like this:
123 Joe Blow Edit
123 Joe Blow New
234 Jane Smith New
345 John Doe New
I understand why this is occurring but is there a way to further filter this resultset down by just using ID?
I want one record to return and the Edit record trumps the New record. So I just want this:
123 Joe Blow Edit
234 Jane Smith New
345 John Doe New
The proper way to handle things in this case isn't a Union. It's to use a left outer join instead of the inner join that LINQ defaults to. That will allow you to select all of the rows a single time and set the flag if data was matched appropriately:
var results = from e in employee
join l in leaders on l.ID equals e.ID into ls
from l in ls.DefaultIfEmpty()
select new {
e.ID,
Name = e.firstName + e.lastName,
flag = (p == null ? "New" : "Edit")
};
You can use GroupBy to group records by their ID and select just one of them:
var results = ldrSet.Union(allEmpSet)
.GroupBy(item => item.ID, (key, items) =>
items.OrderBy(item => item.flag)
.FirstOrDefault());
You can change the contents of OrderBy to determine which of the items to choose.

C# Linq finding value

I want to return the depart number that is not found Employee Table by comparing Department table.
Person Table
ID name salary job commision DeptID
--------------------------------------------------------------
P001 Jon 2000 Manager NULL 1
P002 Skeet 1000 Salesman 2000 1
P003 James 2340 Developer NULL 2
P004 greed 4500 Developer NULL 2
P005 Joel 1330 Salesman 1200 1
P006 Deol 5000 Architect NULL 2
Department Table
DeptID DeptName
1 Management
2 Software
3 ERP
SQL
select DeptId from dept
where deptId not in (select deptid from person)
When i try to execute the below code
LINQ
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID };
I receive the following error
Operator '!=' cannot be applied to operands of type 'int?' and 'System.Linq.IQueryable'
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId).FirstOrDefault()
select new { DeptID = n.DeptID };
You are trying to compare DeptID with a collection 1 or more department Ids. Even if there would only logically be one result for a DeptID, syntactically you need to specify that you want the first hit.
Suggested rephrasing:
var q = from m in context.Depts
where
!context.Persons.Select(p => p.DeptID).Contains(m.DeptID)
select new { DeptID = m.DeptID };
It sounds that your DeptID field in SQL is set to allow nulls. In that case you'd probably want something along the lines of this:
var qry = from n in context.Persons
where n.DeptID.Value !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID.Value };
I think it should be something like that. I tried to get a list of DeptID's first and then implement a NOT IN with contains :
var deptIDs = context.Persons
.Where( p => !context.Depts
.Select(d => new {DeptID = d.DeptID})
.Contains( p.DeptID )
)
.Select( p => new { DeptID = n.DeptID } );

Categories

Resources