Selecting top two of the combined group by linq - c#

I am trying to figure out how to pick the last two "Transactions" from my query.
My Query looks like this
var summary= (from tType in _context.CommandCentre.TransactionTypes
join tSummary in _context.CommandCentre.TransSummary on tType.Id equals tSummary.TransactionType
where tSummary.ChargeFlag.ToLower() == ChargeFlag.Overcharge.ToString().ToLower()
group tSummary by new { tType.Name, tSummary.NumberOfTransactions, tSummary.PeriodDate }
into gResult
select new
{
Fee = gResult.Key.Name,
TransactionCount = gResult.Key.NumberOfTransactions,
Period = gResult.Key.PeriodDate,
ActualAmount = gResult.Sum(x => x.ActualAmount),
}).OrderByDescending(x=>x.Period);
Now if I do a Take(2) I get only the last two records, while I want to get the last two records for every "Fee" of my selection. Basically Two records for every "Fee" ordered by "Period" date.
not sure how to do this in a single query.

Try this :
var result = summary.GroupBy(p => p.Fee)
.SelectMany(d => d.OrderBy(r => r.Period).Take(2))
.ToList();

Related

Group in Linq with Max: How do I get the full data set of the max value?

what I have got so far is this:
var a = from e in tcdb.timeclockevent
group e by e.workerId into r
select new { workerId = r.Key, Date = r.Max(d => d.timestamp) };
This Query is giving me latest "timestamp" of every workerId (Note: workerId is not the primary key of tcdb.timeclockevent). So it is only giving me pairs of two values but I need the whole data sets
Does anybody know how I can get the whole datasets of tcdb.timeclock with the maximal timestamp for every workerId?
OR
Does anybody know how I can get the Id of the data sets of the maximal date for each worker?
Thank you in advance :)
You can order your r grouping by timestamp and select the first one
var a = from e in tcdb.timeclockevent
group e by e.workerId into r
select r.OrderByDescending(d => d.timestamp).FirstOrDefault();
Does anybody know how I can get the whole datasets of tcdb.timeclock with the maximal timestamp for every workerId?
Well, the straightforward query would be like this:
var queryA =
from e in tcdb.timeclockevent
group e by e.workerId into g
let maxDate = g.Max(e => e.timestamp)
select new { workerId = g.Key, events = g.Where(e => e.timestamp == maxDate) };
If you don't need IQueryable<T> result and since there is no SQL construct that returns directly the grouped result set, you could try the following query, which uses a different way of filtering the records with maximal timestamp for every workerId inside the database, and then does the grouping in memory:
var queryB = tcdb.timeclockevent
.Where(e => !tcdb.timeclockevent.Any(e2 =>
e2.workerId == e.workerId && e2.timestamp > e.timestamp))
.AsEnumerable()
.GroupBy(e => e.workerId);
You can try and see which one performs better with your data.

Efficient way of finding multiple dates per ID

I'm trying to query my MsSQL Express database to find all CompanyID's which have multiple dates associated - when I say multiple dates, I must point out they need to be over different days.
EG
ID UkDate CompanyId
1 01/01/2015 16
2 01/01/2015 16
3 03/01/2015 18
4 05/01/2015 19
5 06/01/2015 20
6 08/01/2015 20
In the example above, only the rows with ComapnyID 20 would be returned because it occurred multiple times and those times were over dates (note that although companyId 16 has multiple entries, but both entries are the same date).
I'm not sure how to write the query for this using Linq. My object is already IQueryable<T> but, I'm not sure how to perform the query without executing the code, and then 'finishing off' the query.
I'm not near Visual Studio but the code would be (please forgive typing errors, this is from memory)
//First, grab unique CompanyIds as this removes those who didn't visit multiple times
var uniqueIds = (from d in this._database.MyTable
select companyId).Distinct();
//This is the problem because on each iteration I'm re-querying the database!
foreach(var id in uniqueIds)
{
var result = (from d in this._database.MyTable.OrderBy(a=>a.UkDate)
where d.CompanyId==id
select d);
//check for nulls
if (result.First(a=>a.UkDate.Day) != result.Last(a => a.UkDate.Day)
{
this.AllResultsList.AddRange(results);
}
}
Whilst it works without error I don't feel the code is correct - it feels like a hack and unefficient but this was my best effort. Is there a way I could reduce the number of database requests I make and achieve the same result
It would be something along the lines of
var results = myTable.GroupBy(x => x.CompanyID)
.Where(g => g.GroupBy(g2 => g2.UkDate).Count()>1)
.Select(g => g.Key);
Live example (albeit with LinqToObjects, but the query should work against a database just fine): http://rextester.com/FPHI53553
var results = (from o in this._database.MyTable
group o by o.CompanyId into grouped
where (grouped.Max(s => s.UKDate) - grouped.Min(s => s.UKDate)).TotalDays > 0
select grouped.Key);
Edit (by OP)
Final result:
var results = (from o in this._database.MyTable
group o by o.CompanyId into grouped
where (Convert.ToDateTime(grouped.Max(s => s.UKDate)) - Convert.ToDateTime(grouped.Min(s => s.UKDate))).TotalDays > 0
from l in myTable
where l.CompanyID == grouped.Key
select l).ToList();
A little different version:
var result = (from o in this._database.MyTable
group o by o.CompanyId into grouped
select new {
grouped.Key,
Count = grouped.Select(c => c.UkDate).Distinct().Count()
} into filter
where filter.Count > 1
join a in this._database.MyTable on filter.Key equals a.CompanyID
select new { a.CompanyID, a.UkDate}
).ToList();
You can also try this if you want the company id and a count of the different dates:
from c in dataTable
group c by c.CompanyId into grouped
let count = grouped.Select(x => x.UkDate).Distinct().Count()
where count > 1
select new { CompanyId = grouped.Key, Count = count }

LINQ Groupby query on a single column

I have a situation where I have a Job which has multiple tests which run at specific intervals. A job run generates a unique TestRunId which is a GUID, which is used to reference multiple results, basically grouping a particular run with a unique RunId(GUID).
Now the problem is that I need to select unique runs which have been generated, but my LINQ query picks up every run.
I tried something like this
var testRunIds = ((from tests in context.testresults
where tests.JobId == jobId
select new
{
tests.TestRunId
}).GroupBy(t=>t.TestRunId).OrderBy(t=>t.Key).Skip((pagenum - 1) * pagesize).Take(pagesize)).ToList();
But as I said, this query picks up each and every testResult. Not sure how I do this now. I tried Distinct(), but that too didnt work. Sample data below.
Thanks
I believe the problem is that I have multiple TestRunId values as its essentially a grouping. Inorder to achieve what I need, I tried using (got using Linqer)
from Queries in db.TestResult
where
Queries.JobId == 1
group Queries by new {
Queries.TestRunId,
Queries.StartTime,
Queries.EndTime
} into g
orderby
g.Key.TestRunId
select new {
_ID = (int?)g.Max(p => p.Id),
g.Key.TestRunId,
g.Key.StartTime,
g.Key.EndTime
}
But this works only for MSSQL datasource which is essentially a
SELECT max(id)[ ID],
TestRunId,
StartTime,
Endtime
FROM dbo.query where jobid = 1 group by TestRunId,StartTime,Endtime order by StartTime;
But what I need is
SELECT TestRunId,StartTime,Endtime FROM testresult where jobid = 1 group by TestRunId order by StartTime;
for MySQL.
Try this:-
var jobs = context.testresults;
var query2 = jobs.Where(x => x.TestID == 1).OrderBy(x => x.StartTime).Select(x => x.TestRunID).Distinct();
Working Fiddle.
I think you're possibly looking for this:
var testRunIds = context.testresults.Where(t => t.JobId == jobId).OrderBy(t => t.starttime)
.Select(t => t.TestRunId).Distinct().Skip((pagenum - 1) * pagesize).Take(pagesize)
.ToList();
Do the filtering and ordering first, then select the single field needed, then use Distinct() for uniqueness, then skip/take as required. Selecting the single field first then attempting to order or filter on other fields in the table won't work as those fields are no longer part of the query.
Thanks for helping me out. I managed to do this in a two step process.
var testRunIds = (from tests in context.testresults
where tests.JobId == jobId
select new
{
tests.TestRunId,
tests.StartTime
}).OrderBy(x => x.StartTime).Skip((pagenum - 1) * pagesize).Take(pagesize).GroupBy(x=>x.TestRunId).ToList();
var resultData = testRunIds.Select(testRunId => (context.testresults.Where(
items => items.TestRunId == testRunId.Key)).FirstOrDefault()).ToList();

Linq lambda expression many to many table select

I have three tables, which two of them are in many to many relationship.
Picture:
This is the data in middle mm table:
Edit:
Got until here, I get proper 4 rows back, but they are all the same result(I know I need 4 rows back, but there are different results)
return this._mediaBugEntityDB.LotteryOffers
.Find(lotteryOfferId).LotteryDrawDates
.Join(this._mediaBugEntityDB.Lotteries, ldd => ldd.LotteryId, lot => lot.Id, (ldd, lot) =>
new Lottery
{
Name = lot.Name,
CreatedBy = lot.CreatedBy,
ModifiedOn = lot.ModifiedOn
}).AsQueryable();
My question is, how can I retrieve all the Lotteries via many to many table WHERE I have LotteryOfferId given only?
What I want to achieve is to get data from lottery table by LotteryDrawDateId.
First I use LotteryOfferId to get DrawDates from middle table, and by middle table I get drawDateIds to use them in LotteryDrawDate table. From that table I need to retreive Lottey table by LotteryId in LotteryDrawDate table.
I gain this by normal SQL(LotteryOffersLotteryDrawDates is middle table in DB, not seen in model):
select
Name, Lotteries.CreatedBy, Lotteries.ModifiedOn, count(Lotteries.Id)
as TotalDrawDates from Lotteries join LotteryDrawDates on Lotteries.Id
= LotteryDrawDates.LotteryId join LotteryOffersLotteryDrawDates on LotteryDrawDates.Id =
LotteryOffersLotteryDrawDates.LotteryDrawDate_Id
where LotteryOffersLotteryDrawDates.LotteryOffer_Id = 19 group by
Name, Lotteries.CreatedBy, Lotteries.ModifiedOn
But Linq is different story :P
I would like to do this with lambda expressions.
Thanks
db.LotteryOffer.Where(lo => lo.Id == <lotteryOfferId>)
.SelectMany(lo => lo.LotteryDrawDates)
.Select( ldd => ldd.Lottery )
.GroupBy( l => new { l.Name, l.CreatedBy, l.ModifiedOn } )
.Select( g => new
{
g.Key.Name,
g.Key.CreatedBy,
g.Key.ModifiedOn,
TotalDrawDates = g.Count()
} );
You can do this:
var query = from lo in this._mediaBugEntityDB.LotteryOffers
where lo.lotteryOfferId == lotteryOfferId
from ld in lo.LotteryDrawDates
group ld by ld.Lottery into grp
select grp.Key;
I do this in query syntax, because (in my opinion) it is easier to see what happens. The main point is the grouping by Lottery, because you get a number of LotteryDrawDates any of which can have the same Lottery.
If you want to display the counts of LotteryDrawDates per Lottery it's better to take a different approach:
from lot in this._mediaBugEntityDB.Lotteries.Include(x => x.LotteryDrawDates)
where lot.LotteryDrawDates
.Any(ld => ld.LotteryDrawDates
.Any(lo => lo.lotteryOfferId == lotteryOfferId))
select lot
Now you get Lottery objects with their LotteryDrawDates collections loaded, so afterwards you can access lottery.LotteryDrawDates.Count() without lazy loading exceptions.

Show 3 result from each element in group by

I am working on a system for handling meter reading.
I want to produce a output where the system displays all the meters belonging to the customer and for each meter, the three last readings.
So far, I have to followering code:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
select new
{
Consumption = result.Key.Consumption, No = result.Key.MeterNumber, Date = result.Key.Date
};
Now, it shows all the meters belonging to the customer. If I put a .take(3), it only shows the first 3 results.
Thx!
Daniel
I think what you need is to put the .Take(3) in the right place.
In you case you probably did result.Take(3) but this means take the first three groups (with all their elements).
Below is an attempt to show what I mean, however, I suppose you will need to fix it in the last part, as I don't have data to test it on, and as such I'm not sure if what I'm trying to access is accessible at that point. But I hope you get what I mean.
var lastMeterReading = (from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result
select new {Key = m.Key, Info = result.OrderByDescending(r => r.Date).Take(3)})
.Select(r => new
{ Consumption = r.Consumption, No = r.MeterNumber, Date = r.Date });
Try this:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result.Take(3)
select new
{
Consumption = m.Consumption, No = m.MeterNumber, Date = m.Date
};
You only want to group by MeterNumber. The way you're doing the grouping right now, you'll get a new group for every unique MeterNumber-Consumption-Date combination.
You can also simplify your query using LINQ's GroupJoin operator. In query syntax you use the "join..on..into" pattern:
from meter in meterModel.Meters
where (meter.CustNo == 6085574)
join reading in meterModel.Readings
on meter.MeterNumber equals reading.MeterNumber
into meterGroup
select meterGroup.OrderByDescending(r => r.Date).Take(3);
Or using dot notation:
meterModel.Meters
.Where(x => x.CustNo == 6085574)
.GroupJoin(
meterModel.Readings,
meter => meter.MeterNumber,
reading => reading.MeterNumber,
(meter,readings) => readings.OrderByDescending(r => r.Date).Take(3))
;

Categories

Resources