Linq groupby and global count (not count per group) - linq

I have the following query:
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost)
};
It returns all the incidents aggregated by month and year. But I need the total number of incidents too. Not the incidents for every group, but the total count. The sum of the incidents of all groups. Can this be done in this query or is it better to just roll another query just to get the total global count?

I believe you could get the sum per group and then sum those. Something like
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost),
Count = g1.Count()
};
var total = queryResult.SUM(x=>x.count);
The other alternative I see is to join a sub query see Join Subquery result in Linq
something like
var subquery = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
where vehicle.EnterpriseId == enterpriseId
into g1
select new
{
Total = g1.Count()
};
var query = from incident in _dbContext.VehicleIncidents
join vehicle in _dbContext.Vehicles on incident.VehicleId equals vehicle.Id
join sub in subquery on 1=1
where vehicle.EnterpriseId == enterpriseId
group incident by new {incident.ReportDate.Month, incident.ReportDate.Year}
into g1
orderby g1.Key.Year, g1.Key.Month
select new
{
Month = g1.Key.Month,
Year = g1.Key.Year,
Cost = g1.Sum(o => o.Cost),
Count = sub.total
};

Related

following linq performing left outer join instead of inner join

I cannot see where the problem lies with the following code. I am trying to retrieve those employees who are named as responsibles for certain vacancie. I have about 20 vacancies in my DB assigned to some 16 employees and about 1801 employee records in the employees table. The code always returns a result with 1801 entries.
from emp in container.Employees
join p in container.Vacancies
on emp.EMPID equals p.ResponsibleOfficer into j
group j by new {k1=emp.EMPID,k2=emp.NAME} into g
select new { EmpId = g.Key.k1, Name = g.Key.k2 , Count = g.Count()}
I want something similar to this
select emp.EmpId,emp.Name,Count(*) as count
from Vacancies p, Employees e
where p.ResponsibleOfficer=e.EmpId
group by e.EmpId,e.Name
any help is much appreciated. thanks
You're using join ... into. That will always return a single result for each element of the original sequence, even if there are no matches in the right sequence.
You can filter out entries with no elements in j using a where clause:
from emp in container.Employees
join p in container.Vacancies
on emp.EMPID equals p.ResponsibleOfficer into j
where j.Any()
group j by new {k1=emp.EMPID,k2=emp.NAME} into g
select new { EmpId = g.Key.k1, Name = g.Key.k2 , Count = g.Count()}
Or you could just use an inner join to start with - but I don't understand your current grouping well enough to see what you're trying to do. What is your group by clause for?
EDIT: If it was really just to group by employee, you're already doing that. You can change the code to:
from emp in container.Employees
join p in container.Vacancies
on emp.EMPID equals p.ResponsibleOfficer into j
where j.Any()
select new { Employee = emp, Count = j.Count()}
Basically, after the join you've got two range variables in scope: emp (the "current" employee) and j (all the relevant vacancies matching that employee). You're just trying to count j for each employee, right?
I'm using lambda, but works:
container
.Employees
.Join(container.Vacancies, l => l.EmpId, e => e.ResponsibleOfficer, (l, e) => new { l.EmpId, l.Name })
.GroupBy(g => new { g.EmpId, g.Name })
.Select(s => new { EmpId = s.Key.EmpId, Name = s.Key.Name, Count = s.Count() });

Linq query with two sub-queries that group by, one with an average, and one with a max

I have a parent table, parentTable which may or may not have children in childTable. I am looking to get average % complete of any given parent's children, and the MAX(due) (date) of the children where they exist. My SQL is this:
SELECT parentRecord_id, assigned_to,
(SELECT avg(complete)
FROM childTable
WHERE parent_id = parentRecord_id
and deleted IS NULL
GROUP BY parent_id),
(SELECT max(due)
FROM childTable
WHERE parent_id = parentRecord_id
and deleted IS NULL
GROUP BY parent_id
)
FROM parentTable s
WHERE s.deleted IS NULL and assigned_to IS NOT NULL
My result set gives me rows with either correct values for the average and max, or null. In this instance I have to do follow up processing so I could ignore the null values if I was doing a foreach through DataTable rows. However I am trying to do this in Linq and can't figure out how to avoid a System.InvalidOperationException where Linq is trying to cast null to a double. Here is what I've tried so far.
var query8 = from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
((from t in db.childTable
where t.parent_id == s.strategy_id
group t by new { t.parent_id } into g
select new
{
a0 = g.Average(f0 => f0.complete )
}).FirstOrDefault().a0)
};
foreach (var itm in query8)
{
Console.WriteLine(String.Format("User id:{0}, Average: {1}", itm.ID, itm.Average));
}
Here's my question. How do I get the query to handle those returned rows where average complete or max due (date) are null?
You can either filter out the records where the values are null (by another condition) or if you want to include them do something like this:
a0 = g.Average(f0 => f0.complete.HasValue? f0.complete: 0 )
I would cast the list to nullable double before calling Average/Max like so:
var query8 =
from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
from t in db.childTable
where t.parent_id == s.strategy_id
group t by t.parent_id into g
select g.Cast<double?>().Average(f0 => f0.complete)
};
Assuming complete is a Nullable, you should be able to do:
var query8 = from s in db.parentTable
where s.deleted == null
select new
{
ID = s.assigned_to,
Average =
((from t in db.childTable
where t.parent_id == s.strategy_id
&& s.complete.HasValue()
group t by new { t.parent_id } into g
select new
{
a0 = g.Average(f0 => f0.complete )
}).FirstOrDefault().a0)
};
Thanks to all who responded.
I was unable get around the null anonymous issue with the basic query as I had it, but adding a join to the childTable eliminated the nulls.
Another solution is to use a from x in g.DefaultIfEmpty clause.
var query8 =
from st in db.tableParent
select new { Ass = st.assigned_to ,
Avg =
(from ta in db.tableChild
group ta by ta.parent_id into g
from x in g.DefaultIfEmpty()
select g.Average((f0=>f0.complete))).FirstOrDefault()
};

LINQ Query using 3 tables to get grouped data

So I am trying to get a list of cities and the name of the best sold product in each city. There are 3 tables and I cant seem to group them properly and get the count.
Here is what I have so far:
var result9 = (from p in shop.Tb_Purchases
join c in shop.Tb_PreferredCustomer on p.Cust_ID equals c.Cust_ID
join ap in shop.Tb_AvailableProduct on p.Prod_ID equals ap.Prod_ID
group ap by new { c.City, ap.Name } into g
select new { City = g.Key.City, Name = g.Key.Name, NumOf = g.Count() }).ToList();
and this gives me every product sold in each city and how many of them were sold, however i need only one city and the one product that was sold the most in it.
One solution is to group by just the city and then find each city's best product in a subquery.
var result9 = (from p in shop.Tb_Purchases
join c in shop.Tb_PreferredCustomer on p.Cust_ID equals c.Cust_ID
join ap in shop.Tb_AvailableProduct on p.Prod_ID equals ap.Prod_ID
group ap by c.City into g
let largestProductGroup = g.GroupBy(x => x.Name)
.OrderByDescending(x => x.Count())
.First()
select new
{
City = g.Key.City,
Name = largestProductGroup.Key.Name,
NumOf = largestProductGroup.Count()
}).ToList();

LINQ count query returns a 1 instead of a 0

I have the following view:-
CREATE VIEW tbl_adjudicator_result_view
AS
SELECT a.adjudicator_id, sar.section_adjudicator_role_id, s.section_id, sdr.section_dance_role_id, d.dance_id, c.contact_id,
ro.round_id, r.result_id, c.title, c.first_name, c.last_name, d.name, r.value, ro.type
FROM tbl_adjudicator a
INNER JOIN tbl_section_adjudicator_role sar on sar.section_adjudicator_role2adjudicator = a.adjudicator_id
INNER JOIN tbl_section s on sar.section_adjudicator_role2section = s.section_id
INNER JOIN tbl_section_dance_role sdr on sdr.section_dance_role2section = s.section_id
INNER JOIN tbl_dance d on sdr.section_dance_role2dance = d.dance_id
INNER JOIN tbl_contact c on a.adjudicator2contact = c.contact_id
INNER JOIN tbl_round ro on ro.round2section = s.section_id
LEFT OUTER JOIN tbl_result r on r.result2adjudicator = a.adjudicator_id AND r.result2dance = d.dance_id
When I run the following query directly against the db I get 0 in the count column where there is no result
select adjudicator_id, first_name, COUNT(result_id)
from tbl_adjudicator_result_view arv
where arv.round_id = 16
group by adjudicator_id, first_name
However when I use LINQ query I always get 1 in the Count Column
var query = from arv in db.AdjudicatorResultViews
where arv.round_id == id
group arv by new { arv.adjudicator_id, arv.first_name} into grp
select new AdjudicatorResultViewGroupedByDance
{
AdjudicatorId = grp.Key.adjudicator_id,
FirstName = grp.Key.first_name,
Count = grp.Select(p => p.result_id).Distinct().Count()
};
What do I need to change in the View / Linq query.
You're not doing the same thing in the LINQ query as in the SQL. COUNT(result_id) does not count distinct values of result_id - it counts non-null values.
Try this instead:
Count = grp.Select(p => p.result_id).Where(x => x != null).Count()
The point is: you're grouping your data in the LINQ query - and you'll always get at least one group.
That group's Count may be 0 - but the count of groups will be 1.

Linq query help

I'm attempting to write a linq query which uses several tables of related data and have gotten stuck.
The expected result: I need to return the three most populous metropolitan areas per region by population descending.
tables w/sample data:
MetroAreas -- ID, Name
2, Greater New York
Cities -- ID, Name, StateID
1293912, New York City, 10
CityPopulations -- ID, CityID, CensusYear, Population
20, 1293912, 2008, 123456789
21, 1293912, 2007, 123454321
MetroAreaCities -- ID, CityID, MetroAreaID
1, 1293912, 2
States -- ID, Name, RegionID
10, New York, 5
Regions -- ID, Name
5, Northeast
I start with the metro areas. Join the MetroAreaCities to get city IDs. Join Cities to get state IDs. Join States to get the region ID. Join regions so I can filter with a where. I get stuck when I try to include CityPopulations. I only want the three most populous metro areas for a given region. Doing a simple join on the cityPopulations returns a record per year.
(Here's what I have so far, this query was written for SubSonic 3):
return from p in GeoMetroArea.All()
join q in GeoMetroAreaCity.All() on p.ID equals q.MetroAreaID
join r in GeoCity.All() on q.CityID equals r.ID
join s in GeoState.All() on r.StateID equals s.ID
join t in GeoRegion.All() on s.RegionID equals t.ID
where t.ID == regionObjectPassedToMethod.ID
select p;
Can anyone help me with this query or point me in the right direction? Thank you very very much.
I haven't compiled it, but this should get you close:
var regionID = 5;
var year = (from c in GeoCityPopulation.All()
select c.CensusYear
).Max();
var metros =
// States in Region
from s in GeoStateAll()
where s.RegionID == regionID
// Cities in State
join c in GeoCity.All() on s.CityID equals c.ID
// Metro Area for City
join mc in GeoMetroAreaCity.All() on c.ID equals mc.CityID
// Population for City
join cp in GeoCityPopulation.All() on c.ID equals cp.CityID
where cp.CensusYear = year
// Group the population values by Metro Area
group cp.Population by mc.MetroAreaID into g
select new
{
MetroID = g.Key, // Key = mc.MetroAreaID
Population = g.Sum() // g = seq. of Population values
} into mg
// Metro for MetroID
join m in GeoMetroArea.All() on mg.MetroID equals m.ID
select new { m.Name, mg.Population };

Resources