Linq Grouping Order By Date then Time - c#

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

Related

Converting SQL to LINQ with Multiple Tables and Group

There seem to be lots of questions about SQL to LINQ, but I can't seem to find examples with joined tables and grouping; specifically with a need to get data from multiple tables.
Take this simple SQL:
SELECT
s.showId, s.showName, v.venueName, Min(dateTime) startDate
FROM
shows s
INNER JOIN venues v ON s.venueId = v.venueId
INNER JOIN showDates d ON s.showId = d.showId
GROUP BY
s.showId
The best I can come up with is the following
var ungrouped = (
from s in db.Shows
join v in db.Venues on s.VenueId equals v.VenueId
join d in db.ShowDates on s.ShowId equals d.ShowId
select new { s, v, d }
).ToList();
var grouped = (
from s in ungrouped
group s by s.s.ShowId into grp
select new
{
showId = grp.Key,
name = (from g in grp select g.s.showName).FirstOrDefault(),
venue = (from g in grp select g.v.VenueName).FirstOrDefault(),
startDate = grp.Max(g => g.d.DateTime)
}
);
This works but it feels messy. I don't like:
It being split into two statements
Having to repeatedly write (from g in grp select ...).FirstOrDefault()
Bits like s.s.ShowId
How its vastly more lines of code than the SQL
This example is a simple one, it only gets worse when I have 5+ tables to join and 10+ columns to select.
Question: Is this the best way to do this, and I should just accept it; or is there a better way to write this query?
I am not sure if you are looking for something like this but it's a bit cleaner, it's not split in 2 statements and you might find it helpful. I couldn't use a dbcontext so I used lists to make sure the syntax is correct.
var res = Shows.Join(Venues,
show => show.VenueID,
venue => venue.VenueID,
(show, venue) => new { show, venue })
.Join(ShowDates,
val => val.show.ShowID,
showdate => showdate.ShowID,
(val, showDate) => new { val.show, val.venue, showDates = showDate })
.GroupBy(u => u.show.ShowID)
.Select(grp => new
{
showId = grp.Key,
name = grp.FirstOrDefault()?.show.showName,
venue = grp.FirstOrDefault()?.venue.VenueName,
startDate = grp.Max(g => g.showDates.DateTime)
});
we need to now realation beetwen them one to one or one to many , but not too far from this answer.
var GrouppedResult = Shows.Include(x=>x.Veneu).Include(x=>x.ShowDates)
.Where(x=>x.Veneu.Any()&&x.ShowDates.Any())
.GroupBy(x=>x.ShowId)
.Select(x=>///anything you want);
or
from show in Shows
join veneu in Veneu on veneu.VeneuId equals show.VeneuId
join showDates in ShowDates on showDates.ShowId=show.ShowID
group show by show.Id into grouppedShows
select new { ///what you want };

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

C# query only when textbox is edited

I have a query that work on a great data table. The code for the query is:
var getExtInv = snd.external_invoices.OrderByDescending(x => x.date).ToList();
var query = (from c in getExtInv
join o in snd.invoices on c.idexternal_invoices equals o.id_external_invoice
select new {c.idexternal_invoices,
c.businessname,
o.number,
c.message,
c.price,
c.date,
c.tipologiaPagamento,
c.esitoPagamento,
c.iduser
}).ToList();
I need to filter this query with a number of textbox value that can be empty. An example for one search filter is:
if (txtIdUser.Text != "")
{
int idUserSel = Convert.ToInt32(txtIdUser.Text);
query = query.Where(x => x.iduser == idUserSel).ToList();
}
the problem is that with this approach initially load a very high number of data which then filter based on the presence or absence of textfield filled. In doing so the initial loading time is very long. How can I speed up the process?
Thanks to all
As mentioned, don't use .ToList, .ToArray, .Count, etc. before you are ready to use the results.
int i = 0;
var query = from c in snd.external_invoices.OrderByDescending(x => x.date)
join o in snd.invoices on c.idexternal_invoices equals o.id_external_invoice
select new {c.idexternal_invoices, c.businessname, o.number, c.message,
c.price, c.date, c.tipologiaPagamento, c.esitoPagamento, c.iduser };
if(int.TryParse(txtIdUser.Text, out i) // this will check if text is not empty and valid int
query = query.Where(x => x.iduser == i);
and at the end when you are ready to use the results:
var results = query.ToList();

Get the sum after a linq join

I'm using this linq query to get a list of doubles. The list looks correct, but now I need to total the doubles in the list.
var total = from sauId in schools
join item in entities.AmountTable
on sauId equals item.sauId
select item.amount;
Use Sum() on the result:
var total = (from sauId in schools
join item in entities.AmountTable
on sauId equals item.sauId
select item.amount).Sum();
If item.amount is a double, then total is also a double.
For anyone looking for fluent syntax, I believe this would be the way to do it:
var total = schools
.Join(
entities.AmountTable,
s => s.sauId,
at => at.sauId,
(s, at) => at.amount)
.Sum();
Try to group data by its sauId:
var qry = from item in entities.AmountTable
group item by item.sauId into grp
select new {
sauId = grp.Key,
amuont = grp.Sum(c=>c.amount)
};

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.

Categories