group by in linq doesn't show the expected result - c#

I have these tables :
TestPackagejoint(id,testpackageid,jointid)
Joint(id,endid)
I create a joint between testpackage and joint and joint based on jointid,id .
I want to group the value based on endid and testpackageid,and count of endid or example :
testpackageid endid count
1 2 3
4 2 1
1 3 2
So i write this query
var q = (from i in _ctx.TestPackageJoints
where i.TestPackageId == Id
join joint1 in _ctx.Joints on i.JointId equals joint1.Id
group new {joint1.EndId,i.TestPackageId} by new { i, joint1}
into g1
select new
{
testpackid = g1.Key.i.TestPackageId,
Count = g1.Count(),
endid = g1.Key.joint1.EndId
}).ToList();
But the result :

You must understand what want to get. You want to get testpackid and endid, so it's correct to group them. You also want to group by testpackid and endid, why do you group by new {i, ...} here.
Try
group new {joint1.EndId,i.TestPackageId} by new {joint1.EndId,i.TestPackageId}
Or
join ...
let item = new {joint1.EndId,i.TestPackageId}
group item by item

Your query ought to be:
var query =
from tpj in _ctx.TestPackageJoints
join j in _ctx.Joints on tpj.JointId equals j.Id
where tpj.TestPackageId == id
group 1 by new { tpj.TestPackageId, j.EndId } into g
select new
{
g.Key.TestPackageId,
g.Key.EndId,
Count = g.Count(),
};
The group by clause is in the form group [object] by [key]. You included a TestPackageJoint object as part of the key which likely doesn't define equality so it produced a group for every pairing.

Related

LINQ group by with multiple fields and aggregate function

I'm trying to make a query in LINQ translating the following T-SQL query:
select b.crs,
d.epg,
d.m,
Count (a.sq_rea) as qt
from t_aaaa a with (nolock)
join t_bbbb b with (nolock) on a.crs = b.crs
join t_cccc c with (nolock) on a.spr = c.spr and c.rsp = 1
join v_dddd d with (nolock) on c.epg = d.epg
Where a.st = 5
group by b.crs, d.epg, d.m
However, I cannot use the aggregate function like this:
var llstData = (from a in lobjaaaaDao.list()
join b in lobjbbbbDao.list() on a.p equals b.p
join c in lobjcccc.listar() on a.u equals c.u
from d in lobjddddDao.list()
where a.w == d.w &&
a.st.Equals(5) &&
d.Indent == false
group a by new { b.xxxx, c.yyyy, c.zzzz } into grp
select new
{
grp.Key.xxxx,
grp.Key.yyyy,
grp.Key.zzzz,
total = a.kkkk.Count() // ERROR ON THIS LINE
}
).ToList();
I am getting this error:
The name a does not exist in th current context
How can I fix the error?
In the last select statement the only parameter from the query you are accessible to is grp; where each group is IGrouping; each group is represented by a key and an IEnumerable which is in your case : IEnumerable<a>. Having said that; you can replace the line total = a.kkkk.Count() with total = grp.SelectMany(v=> v.kkkk).Count() OR total = grp.Sum(a => a.kkkk.Count()) to resolve the issue.
This way total represents the summation of kkkk counts in each a in the group.

LINQ Count returning 1 instead of zero for an empty group

I've got this SQL query:
SELECT oy.ownerId, oy.Year, COUNT(doc.Id) as docCount FROM aavabruf.owneryears oy
left join vastdocuments doc
on oy.ownerId = doc.Ownerid and oy.Year = doc.Year
group by oy.ownerid, oy.year
order by docCount
It shows docCount as ZERO for the OwnerId, Year pairs that have no document match in the vastdocuments table.
I tried to do the same with LINQ using the suggested left outer join solution:
from oy in OwnerYears
join doc in VaStDocuments on new {oy.OwnerId, oy.Year} equals new {doc.OwnerId , doc.Year} into docS
from docIfNull in docS.DefaultIfEmpty()
group oy by new {oy.OwnerId, oy.Year} into g
orderby g.Count() ascending
select new { OwnerId = g.Key.OwnerId, Year = g.Key.Year, docCount = g.Count()}
However, for the OwnerId, Year groups that are not present in the VastDocuments table I get docCount as ONE, not ZERO. If I remove the
from docIfNull in docS.DefaultIfEmpty()
line the "empty" groups will not be shown at all.
How can i get the Count as zero just as it is in the SQL query? I tried the following:
Count = docIfNull == null ? 0 : g.Count()
however in this case I get an error:
The name 'docIfNull' does not exist in the current context
The simplest approach is to count non-null values:
g.Count(x => x != null)
I'd suggest moving the ordering after the select so that you can avoid repeating yourself:
select new { g.Key.OwnerId, g.Key.Year, DocCount = g.Count(x => x != null) } into result
orderby result.DocCount
select result
However, I note that currently you're not using docIfNull at all at the moment... so I suspect your join isn't really doing what you want it to. Perhaps you should be using
group docIfNull by new { oy.OwnerId, oy.Year } into g
?
SQL COUNT function ignores the NULL values, while LINQ Count function w/o predicate counts everything, including nulls.
You can get the same result in LINQ by using the predicate version of Count like this (note the group docIfNull so the g elements will be of the same type as docIfNull):
from oy in OwnerYears
join doc in VaStDocuments on new { oy.OwnerId, oy.Year } equals new { doc.OwnerId, doc.Year } into docS
from docIfNull in docS.DefaultIfEmpty()
group docIfNull by new { oy.OwnerId, oy.Year } into g
let docCount = g.Count(doc => doc != null)
orderby docCount ascending
select new { OwnerId = g.Key.OwnerId, Year = g.Key.Year, docCount = docCount }
(the let clause is just to reuse the expression in orderby and select).
However in LINQ you have another option - in case the (OwnerId, Year) combination inside OwnerYears is unique as it seems, instead of left outer join pattern followed by group by and Count filtering nulls you could use simple group join operator with regular Count call:
from oy in OwnerYears
join doc in VaStDocuments on new { oy.OwnerId, oy.Year } equals new { doc.OwnerId, doc.Year } into docs
let docCount = docs.Count()
orderby docCount ascending
select new { OwnerId = oy.OwnerId, Year = oy.Year, docCount = docCount }

Convert SQL to LINQ Troubles

I have been stuck on this for an embarrassing day... can't seem to convert this to linq. My issue also is that Attendee can be null.
select c.activityId, count(distinct b.attendeeId)
from Attendee a, sponsor_activity c
left outer join sponsor_attendance b
on c.ActivityId = b.ActivityId
where a.RegistrationId = 62
AND c.SponsorLevelId = 2
group by c.activityId
So far I have this code... but I am not getting distinct values
var activity_count = (from c in db.Sponsor_Activitys
where c.SponsorLevelId == pledgelvl
from a in db.Attendees.DefaultIfEmpty()
where a.RegistrationId == registration
select new { Activityid = c.ActivityId, NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == c.ActivityId) })
.ToList();
Sponsor_Attendance
AttendanceId
AttendeeId
ActivityId
Sponsor_Activity
ActivityId
SponsorLevelId
Attendee
AttendeeId
RegistrationId
Returns:
## ActivityID ## ## NumAttending ##
2 4
3 0
4 2
2 4
3 0
4 2
2 4
3 0
4 2
Currently there are 3 attendees that have a registrationid that matches... so this is why it is repeated 3 times in the output.
First, it helps if your original queries are readable. :)
Query:
SELECT c.activityId
, COUNT(DISTINCT b.attendeeId)
FROM Attendee a
, sponsor_activity c
LEFT OUTER JOIN sponsor_attendance b
ON c.ActivityId = b.ActivityId
WHERE a.RegistrationId = 62 AND
c.SponsorLevelId = 2
GROUP BY c.activityId;
Linq:
var activity_count = (from activity in db.Sponsor_Activitys
where activity.SponsorLevelId == pledgelvl
from attendee in db.Attendees.DefaultIfEmpty()
where attendee.RegistrationId == registration
select new
{
Activityid = activity.ActivityId,
NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == activity.ActivityId)
}).ToList();
My answer:
var query = from activity in db.Sponsor_Activitys
// Left outer join onto sponsor_attendances
join attendance in db.Sponsor_Attendances
on activity.ActivityId equals attendance.ActivityId into g
from q in g.DefaultIfEmpty()
join attendee in db.Attendees
on q.AttendeeId equals attendee.AttendeeId
where attendee.RegistrationId == registration &&
activity.SponsorLevelId == pledgelvl
select new
{
Activityid = activity.ActivityId,
NumAttending = db.Sponsor_Attendances.Count(x => x.ActivityId == activity.ActivityId)
}
Given the cartesian join (typically bad!), this might be a better example on just executing SQL rather than trying to convert to Linq.

Linq query - multiple joins and selecting 2 columns

I am using Entity Framework to work with my DB and I need help forming a LINQ query that will help me to get columns LoginId and AccNumber. I need only 1 LoginId for every single AccNumber, doesn't matter which LoginId it is. As it stands now, there are about 800K LoginIds and only 5000 AccNumber. Therefore, at the end I need 1 AccNumber associated with 1 LoginId. So I should have 5000 rows and 2 columns.
Here are the tables that I need joined:
Item
ItemId, AccNumber, other irrelevant columns
Block_Item
ItemId, BlockId, other irrelevant columns
Bookversion_Block
BookversionId, BlockId, other irrelevant columns
Sudent
LoginId, BookversionId, other irrelevant columns
Ideally, I want to replicate this SQL query using LINQ (just trying to provide as much info as possible)
select
LoginId,
AccessionNumber
from Item I
outer apply (select top 1 * from Block_Item where I.ItemId = ItemId) BI
outer apply (select top 1 * from BookletVersion_Block where BlockId = BI.BlockId) BVB
outer apply (select top 1 LoginId from Student where BookletVersionId = BVB.BookletVersionId) ST
Here is what I tried, however, the results that I get back are not correct and I get back like 183,000 records and it takes like 10 minutes to execute this. This line "var uniques = q.ToList();" is the one taking a long time.
using (var context = new MyContext())
{
context.Database.CommandTimeout = 1200;
var listOfAccessionNumbers = GetListOfAllAccessionNumbers(context);
var q = (from items in context.Items
join blockItem in context.Block_Item
on items.ItemId equals blockItem.ItemId into bi
join bookletVersion in context.BookletVersion_Block
on bi.Select(x => x.BlockId).FirstOrDefault() equals bookletVersion.BlockId into BVB
join student in context.Students
on BVB.Select(x => x.BookletVersionId).FirstOrDefault() equals student.BookletVersionId into st
//'VH098334'
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(),
AccNum = items.AccessionNumber.ToString() });
**var uniques = q.ToList();**
IList<string> listOfLogins = new List<string>();
foreach (var accessionNumber in listOfAccessionNumbers)
{
var i = q.ToList().Find(x => x.AccNum.Contains(accessionNumber));
listOfLogins.Add(i.LoginId);
}
}
Try to separate your linq operations, to hopefully see which one is taking the longest and where its going wrong.
Also, I suggest using a dictionary with accNumber as key and LoginID as value so you can make sure each accNumber is associated with 1 and only 1 LoginID.
Dictionary<string, string> uniques = new Dictionary<string, string>();
var q = (from items in context.Items
join bi in context.Block_Item
on items.ItemId equals bi.ItemId
select items).ToList();
//var first = bi.Select(x => x.BlockId).FirstOrDefault();
var val = (from b in context.BookletVersion_Block.Select(bv => bv.BlockId)
join a in q on b equals q.FirstOrDefault().BlockId into BVB
join c in context.Students on BVB.FirstOrDefault().BookletVersionId equals c.BookletVersionId into st
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(), AccNum = a.AccessionNumber.ToString() });
//if (!uniques.ContainsKey(bi.AccNum))
//uniques.Add(val.AccNum, val.LoginId);
uniques = val.ToDictionary(c => c.AccNum, c => c.LoginId);
Haven't tested it, so let me know the time taken and whether you get correct values.
The dictionary should ensure you get 5000 values, but I am not sure why it is taking so long, but lets fix the number of records problem first.

Convert SQL into linq to sql - group by two columns and request count of a third column

This is how the query looks in SQL:
select count(SnpId_this), DrugId, VariantId from tbl_custom_SNPs_All
join tbl_custom_SNP_Variants on VariantId = SnpsVariantId_this
join tbl_custom_SNP_Drugs_Apelon_NUIs on DrugId = SnpsDrugId_this
group by DrugId, VariantId
Here is my attempt using linq-to-sql:
var drugVariantGroups =
(from a in adminDB.tbl_custom_SNPs_Alls
join v in adminDB.tbl_custom_SNP_Variants
on a.VariantId equals v.SnpsVariantId_this
join d in adminDB.tbl_custom_SNP_Drugs_Apelon_NUIs
on a.DrugId equals d.SnpsDrugId_this
group a by new { a.VariantId, a.DrugId } into dv
select new
{
dv.Key.VariantId,
dv.Key.DrugId,
Entries = dv.Sum()
}).ToList();
looks like dv does not have a definition for sum. How do I access SnpId_this to count it?
Just use Count. You may need to filter any entries where SnpId_this is null if that is a possibility and to precisely match the T-SQL.
Entries = dv.Where(t => t.SnpId_this != null).Count()
You want Count not Sum:
var drugVariantGroups =
(from a in adminDB.tbl_custom_SNPs_Alls
join v in adminDB.tbl_custom_SNP_Variants
on a.VariantId equals v.SnpsVariantId_this
join d in adminDB.tbl_custom_SNP_Drugs_Apelon_NUIs
on a.DrugId equals d.SnpsDrugId_this
group a by new { a.VariantId, a.DrugId } into dv
select new
{
dv.Key.VariantId,
dv.Key.DrugId,
Entries = dv.Count()
}).ToList();
If you want sum you should provide the field to use in Sum function like:
dv.Sum(c=>c.Amount)

Categories

Resources