Return full results linq grouping query - c#

I'm trying to group and still retrieve all the data in the table. I'm still pretty new to Linq and can't seem to tell what I'm doing wrong. I not only want to group the results but I still want to retrieve all the columns in the table. Is this possible?
(from r in db.Form
orderby r.CreatedDate descending
group r by r.Record into myGroup
where myGroup.Count() > 0
where (r.CreatedDate > lastmonth)
where r.Name == "Test Name"
select new { r, myGroup.Key, Count = myGroup.Count() }
)
Some how "r" loses its context or since I grouped "r" it has been replaced. Not sure.

You need to split your task into two steps to accomplish what you want.
Group data
var groupedData = db.Form.Where(item=>item.CreatedDate > lastMonth && item.Name == "Test Name")
.OrderByDescending(item=>item.item.CreatedDate)
.GroupBy(item=>item.Record)
.Select(group => new {Groups = group, Key = group.Key, Count = group.Count()})
.Where(item => item.Groups.Any());
From the grouped data select Form elements
var formElements = groupedData.SelectMany(g => g.Groups).ToList();

Since you're filtering on individual records, you should filter them before grouping rather than after:
(
from r in db.Form
where r.CreatedDate > lastMonth)
where r.Name == "Test Name"
orderby r.CreatedDate descending
group r by r.Record into myGroup
where myGroup.Count() > 0
select new { Groups = myGroup, myGroup.Key, Count = myGroup.Count() }
)
// Groups is a collection of db.Form instances

Related

Select one of each matching results from group last record from date

I have multiple customers that are a part of a group designated by a group id.
I would like to retrieve 1 record from a related table for each of the matching group members (last record before a certain date).
Currently I query for a list of group members then for each member i run another query to retrieve last record from a date.
I would like to do this with one query since i can pull up the associated table records using group id - however this returns all the records associated to group (bad).
If i use first or default i only get results for first group found.
I want 1 record from each group member.
My Code (returns all associated records of group members):
List<Record> rs = (from x in db.Records where (x.Customer.Group == udcg && x.CloseDate < date && x.CloseDate < earlyDate) orderby x.CloseDate descending select x).ToList();
But i just want one from each instead of all.
Code I use now:
var custs = (from x in db.Customers where (x.group == udcg) select new { x.CustomerID }).ToList();
expected = custs.Count();
foreach (var cust in custs)
{
Record br = (from x in db.Records where (x.Customer.CustomerID == cust.CustomerID && x.CloseDate < date && x.CloseDate < earlyDate)) orderby x.CloseDate descending select x).FirstOrDefault();
if (br != null)
{
total = (double)br.BillTotal;
cnt++;
}
}
I think this could work
db.Customers
.Where(c => c.group == udcg)
.Select(c => db.Records
.Where(r => r.Customer.CustomerID == c.CustomerID)
.Where(r => r.CloseDate < date)
.Where(r => r.CloseDate > date.AddMonths(-2))
.OrderByDescending(r => r.CloseDate)
.FirstOrDefault())
.Where(r => r != null)
It is translated into one sql query. That means it uses one roundtrip to the server. That could be quite a big difference in performace when compared to the foreach loop. If you look at the generated sql, it would be something like
SELECT some columns
FROM Customers
OUTER APPLY (
SELECT TOP (1) some columns
FROM Records
WHERE some conditions
ORDER BY CloseData DESC
)
In terms of performace of the query itself, I would not expect problems here, sql server should not have problems optimizing this form (compared to other ways you could write this query).
Please try this one, evaluate records list.
DateTime certain_date = new DateTime(2018, 11, 1);
List<Record> records = new List<Record>();
var query = records.GroupBy(x => x.Customer.Group).Select(g => new { Group = g.Key, LastRecordBeforeCertainDate = g.Where(l => l.CloseDate < certain_date).OrderByDescending(l => l.CloseDate).FirstOrDefault() });

Fields not visible after groupby?

What I have now:
var batch_pymnts2 = (from a in ctx.WarehouseStatementBatchPayments
join b in ctx.WarehouseStatementBatches on a.WarehouseStatementBatchID equals b.ID
join c in ctx.WarehousePaymentInvoices on a.ID equals c.WarehouseStatementBatchPaymentID
where b.ID == batchID
select new
{
PaymentId = a.ID,
PaymentNet = a.Net,
PaymentType = a.Type
})
.GroupBy(d => d.PaymentId).Where(x => x.Count() == 1);
I need to query these results like so:
var test = (from a in batch_pymnts2 where a.PaymentNet > 100 select a).ToList();
However, I cant see the fields of the (anonymous) type that the first statement uses to project the results into.
Will I need to use a defined type in the query for the projection? Is there a way to do it with anonymous types?
[update]
I managed to change the source query a bit, moving the group by inside and before the group by. This lets the fields of the anonymous type being projected, be "exposed" in further statements.
var count2 = (from a in WarehouseStatementBatchPayments
join b in WarehouseStatementBatches on a.WarehouseStatementBatchID equals b.ID
join c in WarehousePaymentInvoices on a.ID equals c.WarehouseStatementBatchPaymentID
group a by a.ID into grp
from d in grp
where d.WarehouseStatementBatchID == batchID && grp.Count() == 1
select new { PaymentId = d.ID, PaymentNet = d.Net, PaymentType = d.Type }).ToList();
batch_pymnts2 is a sequence of group objects. In effect, it is a collection of collections of your anonymous type. Each item in batch_pymnts2 has this:
group.Key; /* a PaymentId value */
((IEnumerable)group); /* the anon type items grouped together in this group */
Those group objects implement the IGrouping interface. Their Key property is the PaymentId values that define the groups. If you enumerate the groups (they implement IEnumerable<T>), you'll get the anonymous objects that you grouped by PaymentId:
var test = batch_pymnts2.SelectMany(g => g.Where(anon => anon.PaymentNet > 100));
test is now an enumeration of your anonymous type, because we have now enumerated a subset of the anon items from each of the groups, and (in effect) unioned all those little enumerations of anon back into one big one.
If you want to select groups which have at least one anonymous thingy with PaymentNet > 100, try this:
// Groups which have at least one PaymentNet > 100
var t2 = batch_pymnts2.Where(g => g.Any(anon => anon.PaymentNet > 100));
// PaymentIds of the groups which have at least one PaymentNet > 100
var ids = t2.Select(g => g.Key);
// PaymentIds that appear only once
var singles = t2.Where(g => g.Count == 1).Select(g => g.Key);
I don't know why you're grouping them, or what your PaymentNet > 100 query is meant to accomplish, so I'm not sure exactly how to write the query you want. But your starting point is that you're querying a sequence of group objects which contain enumerations of your anonymous type -- not a sequence of that type itself.

How to filter entity framework result with multiple columns using a lambda expression

I have the following table:
And the following data:
How can i filter the result, so that i only get the latest row from each omraade_id (sorted descending by timestamp)?
Which in this case would be the rows with id: 1010 and 1005
--
From #lazyberezovsky's answer, i have created the following expression:
dbConnection = new ElecEntities();
var query = from data in dbConnection.Valgdata
orderby data.timestamp descending
group data by data.omraade_id into g
select g.FirstOrDefault();
return query.ToList();
It returns two rows with the ID 3 and 4, which are the first two rows in the database, and also the ones with the lowest timestamp. Any idea why?
var query = dbConnection.Valgdata
.GroupBy(x => x.omraade_id)
.Select(g => g
.OrderByDescending(x => x.timestamp)
.FirstOrDefault());
I have no experience with EF, so I'm unsure if only SQL-esque linq works here. A plain C#-ish:
var query = dbConnection.Valgdata.GroupBy(u => u.omraade_id)
.Select(x => x.FirstOrDefault(y => x.Max(p => p.timestamp) == y.timestamp));
You have put filter on every item. It should be applied on complete query result, not on every item.
Following is updated query.
var query = (from data in dbConnection.Valgdata
orderby data.timestamp descending
group data by data.omraade_id into g
select g).FirstOrDefault();
var query = from v in dbConnection.Valgdata
orderby v.timestamp descending
group v by v.omraade_id into g
select g.First();
This will return only record with max timestamp for each omraade_id.
UPDATE query above works fine to me (at least for MS SQL Linq provider). Also you don't need to do FirstOrDefault - if omraade_id is grouped, then it definitely has at least one row.
var query = from v in dbConnection.Valgdata
group v by v.omraade_id into g
select g.OrderByDesc(x => x.timestamp).First();
This is my solution so far:
var data = dbConnection.Valgdata.Where(x => x.godkendt == false).ToList();
var dataGrouped = data.GroupBy(x => x.omraade_id).ToList();
List<Valgdata> list = new List<Valgdata>();
foreach (var grpdata in dataGrouped)
{
var dataGroup = grpdata.OrderByDescending(x => x.timestamp).ToList();
list.Add(dataGroup.FirstOrDefault());
}
return list;
I dont know if it is the most effective, but it works.

Linq Grouping Order By Date then Time

Good Evening,
I've managed to get my Linq query almost correct. There is just one more issue I'm struggling to resolve.
My query is
var o =
(from c in x
group c by x.Date.Date into cc
select new
{
Group = cc.Key.Date,
Items = cc.ToList(),
ItemCount = cc.Count()
}).OrderByDescending(p => p.Group);
Now this query works fine. It groups within a ListView by the date. x.Date is a DateTime field in my SQL Database. Therefore I'm selecting x.Date.Date to Group by the actual Date of the DateTime field, as if it was just x.Date it would Group by the date and time.
My question is, how do I group by time so the newest time is at the top of the group?
Many thanks
Use the linq "ThenBy()" method:
var o = (from c in x group c by x.Date.Date into cc select new
{
Group = cc.Key.Date,
Items = cc.OrderByDescending(y=>y.Date).ToList(),
ItemCount = cc.Count()
})
.OrderByDescending(p => p.Group)
Change Items = cc.ToList() to Items = cc.OrderBy(c => c.[field_you_want_to_sort_by]).ToList()
var o =
(from c in x
group c by c.Date.Date into cc
select new
{
Group = cc.Key.Date,
Items = cc.OrderByDescending(a=>a.Date.Time).ToList(),
ItemCount = cc.Count()
}).OrderByDescending(p => p.Group);

LINQ Using Max() to select a single row

I'm using LINQ on an IQueryable returned from NHibernate and I need to select the row with the maximum value(s) in a couple of fields.
I've simplified the bit that I'm sticking on. I need to select the one row from my table with the maximum value in one field.
var table = new Table { new Row(id: 1, status: 10), new Row(id: 2, status: 20) }
from u in table
group u by 1 into g
where u.Status == g.Max(u => u.Status)
select u
This is incorrect but I can't work out the right form.
BTW, what I'm actually trying to achieve is approximately this:
var clientAddress = this.repository.GetAll()
.GroupBy(a => a)
.SelectMany(
g =>
g.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.AddressReference == g.Max(x => x.AddressReference) &&
a.StartDate == g.Max(x => x.StartDate)))
.SingleOrDefault();
I started with the above lambda but I've been using LINQPad to try and work out the syntax for selecting the Max().
UPDATE
Removing the GroupBy was key.
var all = this.repository.GetAll();
var address = all
.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.StartDate == all.Max(x => x.StartDate) &&
a.AddressReference == all.Max(x => x.AddressReference))
.SingleOrDefault();
I don't see why you are grouping here.
Try this:
var maxValue = table.Max(x => x.Status)
var result = table.First(x => x.Status == maxValue);
An alternate approach that would iterate table only once would be this:
var result = table.OrderByDescending(x => x.Status).First();
This is helpful if table is an IEnumerable<T> that is not present in memory or that is calculated on the fly.
You can also do:
(from u in table
orderby u.Status descending
select u).Take(1);
You can group by status and select a row from the largest group:
table.GroupBy(r => r.Status).OrderByDescending(g => g.Key).First().First();
The first First() gets the first group (the set of rows with the largest status); the second First() gets the first row in that group.
If the status is always unqiue, you can replace the second First() with Single().
Addressing the first question, if you need to take several rows grouped by certain criteria with the other column with max value you can do something like this:
var query =
from u1 in table
join u2 in (
from u in table
group u by u.GroupId into g
select new { GroupId = g.Key, MaxStatus = g.Max(x => x.Status) }
) on new { u1.GroupId, u1.Status } equals new { u2.GroupId, Status = u2.MaxStatus}
select u1;
What about using Aggregate?
It's better than
Select max
Select by max value
since it only scans the array once.
var maxRow = table.Aggregate(
(a, b) => a.Status > b.Status ? a : b // whatever you need to compare
);
More one example:
Follow:
qryAux = (from q in qryAux where
q.OrdSeq == (from pp in Sessao.Query<NameTable>() where pp.FieldPk
== q.FieldPk select pp.OrdSeq).Max() select q);
Equals:
select t.* from nametable t where t.OrdSeq =
(select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)
Simply in one line:
var result = table.First(x => x.Status == table.Max(y => y.Status));
Notice that there are two action.
the inner action is for finding the max value,
the outer action is for get the desired object.

Categories