Linq C# Sum in Group By - c#

I'm trying to transform the SQL that is here http://sqlfiddle.com/#!6/a1c8d/2 in linq below. The expected result is what is in sqlfiddle, but my LINQ returns more rows.
PS: In sqlfiddle the fields are reduced to not increase pollution and stay focused on my problem.
resultado.Dados =
(
from a in db.AgendaHorario
join b in db.Agenda on a.AgendaID equals b.AgendaID
select new
{
a.AgendaID,
Horario = a.Horario,
Controle = a.Controle,
Cor = b.Cor,
Agenda = b.Sigla
}).AsEnumerable()
.GroupBy(g => new
{
g.AgendaID,
Horario = g.Horario.ToString("dd/MM/yyyy"),
Data = g.Horario.ToString("yyyy-MM-dd"),
g.Controle,
g.Agenda,
g.Cor
})
.Select(s => new
{
id = s.Key.AgendaID,
title = s.Key.Agenda,
start = s.Key.Data,
color = String.IsNullOrEmpty(s.Key.Cor) ? "3a87ad" : s.Key.Cor,
className = "",
someKey = 1,
allDay = false,
Resultado0 = s.Sum(m => m.Controle == "L" ? 1 : 0).ToString(),
Resultado1 = s.Sum(m => m.Controle == "B" ? 1 : 0).ToString()
});

As per the comments, this addresses the question of how to repeat your SqlFiddle in Linq. Note that the projection to a String Date cannot be converted to Sql directly, so I've had to early materialize with AsEnumerable() (obviously, in your real query, apply any filters prior to materializing!). You could probably do the grouping on just the date part using SqlFunctions, e.g. 3 x applications of SqlFunctions.DatePart will allow you to group by dd, MM and YYYY
var dados = db.AgendaHorarios1
.AsEnumerable()
.GroupBy(ah => ah.Horario.ToString("dd/MM/yyyy"))
.Select(g => new {Horario = g.Key,
Livre = g.Count(x => x.Controle == "L"),
Bloq = g.Count(x => x.Controle == "B"),
Aged = g.Count(x => x.Controle == "A")});

Related

where clause not working in group by LINQ c sharp

I have a table "register_operation with fields"
[Key]
int id_registru_casa ,
DateTime data ,
int id_cont_sintetic ,
decimal suma ,
string tip
tip can take only 2 value :"receipts" and "payments"
"Groupby" work with no problem
but when I add "where" clause not working
(it doesn't show me any records)
(although there are recordings in database with day 19, month 9 and tip=receipts)
var centralizator_rc = db.register_operation
.Where(i => (i.data.Day == 19) && (i.data.Month == 9) && (tip=="receipts"))
.GroupBy(i => i.id_cont_sintetic)
.Select(g => new {
id_cont_sintetic = g.Key,
total_receipts = g.Sum(i=>i.suma),
}).ToList();
Thanks!
SOLVED!
I change code like this:
var centralizator_rc = db.registru_casa
.Where(crc=>(crc.data.Month==8) && (crc.data.Day==16) && (crc.tip=="receipts"))
.GroupBy(crc=> new
{
crc.id_cont_sintetic,
crc.data.Month,
crc.data.Day,
crc.tip
})
.Select(g => new {
data = ziuaOK,
id_cont_sintetic = g.Key.id_cont_sintetic,
total_incasare = g.Sum(i => i.suma),
}).ToList();

LINQ Query Multiple Group and count of latest record - Oracle DB

I tried to divided Linq queries into 3 (total, success, fail) but so far "Total" Linq query is working fine. Please help me to get "Success", "Fail" columns (it has mulitple statuses and we have to check the last column of each transaction and destination)
Note: you need to group by ProcessTime, TransactionId, Destination and check last column whether it is success or Fail then apply count (we are using oracle as backend)
LINQ for Total count
var query = (from filetrans in context.FILE_TRANSACTION
join route in context.FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
where
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select new { PROCESS_STRT_TIME = DbFunctions.TruncateTime((DateTime)filetrans.PROCESS_STRT_TIME), filetrans.FILE_TRANID, route.DESTINATION }).
GroupBy(p => new { p.PROCESS_STRT_TIME, p.FILE_TRANID, p.DESTINATION });
var result = query.GroupBy(x => x.Key.PROCESS_STRT_TIME).Select(x => new { x.Key, Count = x.Count() }).ToDictionary(a => a.Key, a => a.Count);
Check this solution. If it gives wrong result, then I need more details.
var fileTransQuery =
from filetrans in context.AFRS_FILE_TRANSACTION
where accountIds.Contains(filetrans.ACNT_ID) &&
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select filetrans;
var routesQuery =
from filetrans in fileTransQuery
join route in context.AFRS_FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
select route;
var lastRouteQuery =
from d in routesQuery.GroupBy(route => new { route.FILE_TRANID, route.DESTINATION })
.Select(g => new
{
g.Key.FILE_TRANID,
g.Key.DESTINATION,
ROUTE_ID = g.Max(x => x.ROUTE_ID)
})
from route in routesQuery
.Where(route => d.FILE_TRANID == route.FILE_TRANID && d.DESTINATION == route.DESTINATION && d.ROUTE_ID == route.ROUTE_ID)
select route;
var recordsQuery =
from filetrans in fileTransQuery
join route in lastRouteQuery on filetrans.FILE_TRANID equals route.FILE_TRANID
select new { filetrans.PROCESS_STRT_TIME, route.CRNT_ROUTE_FILE_STATUS_ID };
var result = recordsQuery
.GroupBy(p => DbFunctions.TruncateTime((DateTime)p.PROCESS_STRT_TIME))
.Select(g => new TrendData
{
TotalCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID != 7 && x.CRNT_ROUTE_FILE_STATUS_ID != 8 ? 1 : 0)
SucccessCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID == 7 ? 1 : 0),
FailCount = g.Sum(x => failureStatus.Contains(x.CRNT_ROUTE_FILE_STATUS_ID) ? 1 : 0),
Date = g.Min(x => x.PROCESS_STRT_TIME)
})
.OrderBy(x => x.Date)
.ToList();

Grouping week of the month that is input to linechart

I have a problem when grouping weeks from input months,
the results I get are always like this
{name: "Pembunuhan", data: [1,4]}
it should be the result I want like this
{name: "Pembunuhan", data: [1,0,0,4]}
this is my code
var dateNya = DateTime.Today;
var bln = int.Parse(month);
var mstrKategori = context.master_kategori.OrderBy("id ASC").ToList();
var joinnya = (from ls in context.list_dokumen join ktgr in context.master_kategori on ls.kategori equals ktgr.id
where ls.polda_id != null
select new
{
tgl_laporan = ls.tgl_laporan,
idKategori = ktgr.id,
week = ls.week,
month = ls.month,
year = ls.year
}).ToArray();
foreach (var itemktgr in mstrKategori)
{
var tes2 = joinnya.Where(i => i.idKategori == itemktgr.id).Where(a => a.month == bln).Where(o => o.year == dateNya.Year)
.GroupBy(row => new { week = row.week ?? 0 })
.Select(g => new
{
week = g.Key.week,
couny = g == null ? 0: g.Count()
})
.ToList();
tes2.ToList().ForEach(p => lineChartList.Add(new DataChart {name = itemktgr.nama2, data = p.couny}));
}
var result = lineChartList.GroupBy(x => new { x.name })
.Select(b => new DataChartTrending2
{
data = b.Select(bn => bn.data).ToList(),
name = (b.Key.name == null) ? "Lainnya" : b.Key.name
}).ToList();
The GroupBy clause won't create empty groups for weeks that have no matching records.
Use GroupJoin to perform an outer join on week indices, meaning that you will get a group for each week index, even indices that no record in tes2 matched:
var weekIds = Enumerable.Range(0, 4); // assuming your weeks are 0, 1, 2, 3
var tes2 = joinnya
.Where(i => i.idKategori == itemktgr.id)
.Where(a => a.month == bln)
.Where(o => o.year == dateNya.Year)
var countPerWeek = weekIds.GroupJoin(
tes2,
weekId => weekId,
row => row.week,
(week, weekGroup) => weekGroup.Count()
);
For each week, it will get you the number of matching records, including zeroes for weeks that don't have a matching record.
Alternative syntax:
var countPerWeek =
from weekId in weekIds
join row in tes2 on weekId equals row.week into weekGroup
select weekGroup.Count();

LINQ query to retrieve pivoted data taking too long

I am working on a LINQ query which includes some pivot data as below
var q = data.GroupBy(x => new
{
x.Med.Name,
x.Med.GenericName,
}).ToList().Select(g =>
new SummaryDto
{
Name= g.Key.Name,
GenericName = g.Key.GenericName,
Data2012 = g.Where(z => z.ProcessDate.Year == 2012).Count(),
Data2013 = g.Where(z => z.ProcessDate.Year == 2013).Count(),
Data2014 = g.Where(z => z.ProcessDate.Year == 2014).Count(),
Data2015 = g.Where(z => z.ProcessDate.Year == 2015).Count(),
Data2016 = g.Where(z => z.ProcessDate.Year == 2016).Count(),
Data2017 = g.Where(z => z.ProcessDate.Year == 2017).Count(),
TotalCount = g.Count(),
}).AsQueryable();
return q;
The above LINQ takes too long as it queries grp q.Count()*6 times. If there are 10000 records, then it queries 60000 times
Is there a better way to make this faster?
Add year to the group key, then group again, and harvest per-group counts:
return data.GroupBy(x => new {
x.Med.Name
, x.Med.GenericName
, x.ProcessDate.Year
}).Select(g => new {
g.Key.Name
, g.Key.GenericName
, g.Key.Year
, Count = g.Count()
}).GroupBy(g => new {
g.Name
, g.GenericName
}).Select(g => new SummaryDto {
Name = g.Key.Name
, GenericName = g.Key.GenericName
, Data2012 = g.SingleOrDefault(x => x.Year == 2012)?.Count ?? 0
, Data2013 = g.SingleOrDefault(x => x.Year == 2013)?.Count ?? 0
, Data2014 = g.SingleOrDefault(x => x.Year == 2014)?.Count ?? 0
, Data2015 = g.SingleOrDefault(x => x.Year == 2015)?.Count ?? 0
, Data2016 = g.SingleOrDefault(x => x.Year == 2016)?.Count ?? 0
, Data2017 = g.SingleOrDefault(x => x.Year == 2017)?.Count ?? 0
, TotalCount = g.Sum(x => x.Count)
}).AsQueryable();
Note: This approach is problematic, because year is hard-coded in the SummaryDto class. You would be better off passing your DTO constructor an IDictionary<int,int> with counts for each year. If you make this change, the final Select(...) would look like this:
.Select(g => new SummaryDto {
Name = g.Key.Name
, GenericName = g.Key.GenericName
, TotalCount = g.Sum(x => x.Count)
, DataByYear = g.ToDictionary(i => i.Year, i => i.Count)
}).AsQueryable();
I suggest grouping inside the group by year and then converting to a dictionary to access the counts. Whether it is faster to group with year first and then count in-memory depends on the distribution of the initial grouping, but with the database it may depend on how efficiently it can group by year, so I would test to determine which seems fastest.
In any case grouping by year after the initial grouping is about 33% faster than your query in-memory, but again it is vastly dependent on the distribution. As the number of initial groups increase, the grouping by Year queries slow down to match the original query. Note that the original query without any year counts is about 1/3 the time.
Here is grouping after the database grouping:
var q = data.GroupBy(x => new {
x.Med.Name,
x.Med.GenericName,
}).ToList().Select(g => {
var gg = g.GroupBy(d => d.ProcessDate.Year).ToDictionary(d => d.Key, d => d.Count());
return new SummaryDto {
Name = g.Key.Name,
GenericName = g.Key.GenericName,
Data2012 = gg.GetValueOrDefault(2012),
Data2013 = gg.GetValueOrDefault(2013),
Data2014 = gg.GetValueOrDefault(2014),
Data2015 = gg.GetValueOrDefault(2015),
Data2016 = gg.GetValueOrDefault(2016),
Data2017 = gg.GetValueOrDefault(2017),
TotalCount = g.Count(),
};
}).AsQueryable();

This LINQ-to-Entities join seems overly complex, am I doing something wrong?

I have two queries that I need to combine in LINQ that both actually come from the same table. The reason for this is that one of the queries needs to get the max of a field for each day and then sum the days together where the second query can just sum everything right off the bat. Here is the first query:
var queryDownload = from p in
(from p in almdbContext.cl_contact_event
where p.time_of_contact >= startDate && p.time_of_contact < endDate && p.input_file_name.Contains(inputFileName) && listName.Contains(listName)
group p by new
{
date = EntityFunctions.CreateDateTime(p.time_of_contact.Value.Year, p.time_of_contact.Value.Month, p.time_of_contact.Value.Day, 0, 0, 0),
listName = p.contact_list_name
} into g
select new
{
date = g.Key.date,
listName = g.Key.listName,
download = g.Max(a => a.total_number_of_records)
})
group p by p.listName into g
select new
{
listName = g.Key,
totalDownload = g.Sum(a => a.download),
};
This is the second:
var queryPenData = from p in almdbContext.cl_contact_event
where p.time_of_contact >= startDate && p.time_of_contact < endDate && p.input_file_name.Contains(inputFileName) && listName.Contains(listName)
group p by p.contact_list_name into g
select new
{
listName = g.Key,
dials = g.Sum(a => a.ov_dial_start_time != null ? 1 : 0),
agentConnects = g.Sum(a => a.agent_login_name != null ? 1 : 0),
abandons = g.Sum(a => a.response_status == "DAC" || a.response_status == "DAD" ? 1 : 0),
rightPartyContacts = g.Sum(a => a.response_status == "PTP" || a.response_status == "RPC" ? 1 : 0),
promiseToPays = g.Sum(a => a.response_status == "PTP" ? 1 : 0),
talkTime = g.Sum(a => EntityFunctions.DiffSeconds(a.ov_call_connected_time, a.ov_trunk_released_time)) ?? 0,
wrapTime = g.Sum(a => EntityFunctions.DiffSeconds(a.ov_trunk_released_time, a.record_released_time)) ?? 0
};
And this is the query joining them together.
var queryJoin = from qd in queryDownload
join qp in queryPenData
on qd.listName equals qp.listName
select new
{
listName = qp.listName,
download = qd.totalDownload,
dials = qp.dials,
agentConnects = qp.agentConnects,
abandons = qp.abandons,
rightPartyContacts = qp.rightPartyContacts,
promiseToPays = qp.promiseToPays,
talkTime = qp.talkTime,
wrapTime = qp.wrapTime
};
This seems extremely verbose/roundabout to me. Is there a better way I can write/approach this to shrink/simplify the code?
For your last query could you not just do something like this?
var queryJoin = from qd in queryDownload join qpd in queryPenData on qd.listname equals qpd....
Should be able to do this:
var queryJoin = from qd in queryDownload
join qp in queryPenData
on qd.listName equals qp.listName
select new
{
qp, qd
};
From what I can tell you've already formed your data in queryPenData so there is no reason to re-assign it to new variables in the final join. Just select the object which will allow you to traverse into the anonymous type in queryPenData. I think...

Categories