linq query grouping and joining getting incorrect sum - linq

I have the following code which is grouping and summing some values.
The sum "TotalCost" value is correct, however, when i uncomment the lines the sum value is wrong (its less than it should be)
Im doing something wrong, but cant figure this out. any ideas?
from orderItem in Order_ProductItem
//join ho in Hardware_Items on orderItem.OuterColour equals ho.Index
//join hi in Hardware_Items on orderItem.InnerColour equals hi.Index
where orderItem.SalesOrderID == 3272 && (orderItem.IsDeleted==null || orderItem.IsDeleted.Value == false)
group new { orderItem/*, hi, ho*/} by orderItem.FrameNo into grp
select new OrderItemModel
{
FrameNo = grp.Key,
TotalCost = grp.Sum(x => x.orderItem.SellingPrice),
//InternalColor = grp.FirstOrDefault().hi.Name,
//ExternalColor = grp.FirstOrDefault().ho.Name,
Quantity = grp.FirstOrDefault().orderItem.Quantity,
}
Basic Schema
Order_ProductItem
FrameNo
OuterColour
InnerColour
SellingPrice
Hardware_Items
Index
Name
The Order_ProductItem has FrameNo which is listed multiple times in the table, so im trying to get it to group them, then sum the SellingPrice of each row that has the same FrameNo.
If i exclude the bit to obtain colour (internal and external) the sum is correct.
In that case how can i also include the inner and outer color names?

You probably need to use a left join, because the inner join is filtering out some of your data. Here is an example on how you would change your first join.
join ho in Hardware_Items on orderItem.OuterColour equals ho.Index into hog
from ho in hog.DefaultIfEmpty()

Related

Linq to Sql Left join ang get average value

Let's say i have an Linq query :
var query = from course in _unitOfWork.Course.GetAll()
join candidate in _unitOfWork.CandidateCourses.GetAll() on course.Id equals candidate.Course_id join cr in _unitOfWork.CourseReviews.GetAll() on course.Id equals cr.Course_id into g from rt in g.DefaultIfEmpty()
where candidate.UserId == CandidateId
select new CourseFields { Id = course.Id,
Course_name = course.Course_name,
Course_description = course.Course_description,
Rating = g.Average(x => x.Rating),
TotalSections = sections.Count(), };
Here i am getting exception in calculating Average of Rating, When there is no data in table for that particular course.
Can someone help where i am doing wrong?
Average requires at list one element in the sequence.
You can add a DefaultIfEmpty to set the default rating if no elements exists.
...
Rating = g.DefaultIfEmpty(0).Average(x => x.Rating),
...
If you had rating as a Nullable type (like Nullable<double>>)
then it would not throw an exception but retrun null instead
you can also try this one out. Check for null before doing an average
...
Rating = (g!=null && g.Count()>0)?g.Average(x => x.Rating):0,
...

returning values with a where clause

I have two tables as follows:
ScholarSubject
ScholarSubjectID<pk>
ScholarID
SubjectID
Mark
and
AdmissionReq
SubjectID
DegreeCode
MinumumMark
I'm trying to return everything from a Degree table (with PK degreeID) where the mark for a scholar is less than the minimum mark for admissions. My query is as follows:
public List<object> getDegreeByAPSandRequirements()
{
using (DataLayer.CareerDatabaseEntities context = new DataLayer.CareerDatabaseEntities())
{
return (from Degrees in context.Degrees
join admissions in context.AdmissionReqs on
Degrees.DegreeCode equals admissions.DegreeCode
join subject in context.Subjects on
admissions.SubjectID equals subject.SubjectID
join scholarsubject in context.ScholarSubjects on
subject.SubjectID equals scholarsubject.SubjectID
join scholar in context.Scholars on
scholarsubject.ScholarID equals scholar.ScholarID
where Degrees.APSScore <= scholar.APSScore && admissions.MinimumMark <= scholarsubject.NSC && scholarsubject.SubjectID.Equals(admissions.SubjectID)
select Degrees).Distinct().ToList<object>();
}
}
Everything works, except if I change one of the marks (in ScholarSubject) to a lesser value than the minumum mark (in AdmissionsReq) then it still returns a degree. I want to return a degree if both marks are greater than the minimum requirements and not only one of the marks.
What am I doing wrong? Can someone please help me??
I'm still not sure I understand what you're trying to do - unless you have just one scholar in your database it seems to me you will return a list of all degrees achieved by all scholars. If you don't want this, you need to filter on scholarID in the where clause.
But anyway - in order to get more information I would try to do 2 things.
I would change your query and start it with the scholar not with the degree as this might avoid some duplicates:
from scholar in context.Scholars
join scholarsubject in context.ScholarSubjects on scholar.ScholarID equals scholarsubject.ScholarID
join subject in context.Subjects on scholarsubject.SubjectID equals subject.SubjectID
join admission in context.AdmissionReqs on subject.SubjectID equals admission.SubjectID
join degree in context.Degrees on admission.DegreeCode equals degree.DegreeCode
where degree.APSScore <= scholar.APSScore
&& admission.MinimumMark <= scholarsubject.NSC
//&& scholarsubject.SubjectID.Equals(admission.SubjectID) // you should not need this line as you have the joins in place to assert this
select degree)
.Distinct()
.ToList<object>();
If this produces the same result as your previous query, then I'd change the return type to see what exactly you are getting - replace the last line with this and inspect the collection:
select new {ScholarID = scholar.ScholarID, Degree = degree})
.Distinct()
.ToList();

Greater Than Condition in Linq Join

I had tried to join two table conditionally but it is giving me syntax error. I tried to find solution in the net but i cannot find how to do conditional join with condition. The only other alternative is to get the value first from one table and make a query again.
I just want to confirm if there is any other way to do conditional join with linq.
Here is my code, I am trying to find all position that is equal or lower than me. Basically I want to get my peers and subordinates.
from e in entity.M_Employee
join p in entity.M_Position on e.PostionId >= p.PositionId
select p;
You can't do that with a LINQ joins - LINQ only supports equijoins. However, you can do this:
var query = from e in entity.M_Employee
from p in entity.M_Position
where e.PostionId >= p.PositionId
select p;
Or a slightly alternative but equivalent approach:
var query = entity.M_Employee
.SelectMany(e => entity.M_Position
.Where(p => e.PostionId >= p.PositionId));
Following:
from e in entity.M_Employee
from p in entity.M_Position.Where(p => e.PostionId >= p.PositionId)
select p;
will produce exactly the same SQL you are after (INNER JOIN Position P ON E..PostionId >= P.PositionId).
var currentDetails = from c in customers
group c by new { c.Name, c.Authed } into g
where g.Key.Authed == "True"
select g.OrderByDescending(t => t.EffectiveDate).First();
var currentAndUnauthorised = (from c in customers
join cd in currentDetails
on c.Name equals cd.Name
where c.EffectiveDate >= cd.EffectiveDate
select c).OrderBy(o => o.CoverId).ThenBy(o => o.EffectiveDate);
If you have a table of historic detail changes including authorisation status and effective date. The first query finds each customers current details and the second query adds all subsequent unauthorised detail changes in the table.
Hope this is helpful as it took me some time and help to get too.

multiple grouping, inner joning in Linq

I am trying to translate this into Linq and cannot figure it out:
SELECT
CustomerOrder.ShipState, MONTH(OrderFulfillment.OrderDate) AS Mnth,
YEAR(OrderFulfillment.OrderDate) AS Yer,
SUM(OrderFulfillment.Tax) AS TotalTax
FROM
OrderFulfillment INNER JOIN
CustomerOrder ONOrderFulfillment.OrderID =CustomerOrder.OrderID
WHERE
(OrderFulfillment.Tax > 0)
GROUP BY
CustomerOrder.ShipState, MONTH(OrderFulfillment.OrderDate),
YEAR(OrderFulfillment.OrderDate)
ORDER BY
YEAR(OrderFulfillment.OrderDate) DESC, CustomerOrder.ShipState,
MONTH(OrderFulfillment.OrderDate) DESC
I have Linqpad and have gone through a bunch of the examples but cannot figure this out.
I think you want to do something like this:
from c in CustomerOrder
join o in OrderFulfillment on c.OrderId equals o.OrderId
where
o.Tax > 0
group o by
new { c.ShipState, Mnth = of.OrderDate.Month, Yer = of.OrderDate.Year }
into g
orderby
g.Key.Yer descending, g.ShipState, g.Key.Mnth descending
select
new { g.Key.ShipState, g.Key.Mnth, g.Key.Yer,
TotalTax = g.Sum(i => i.Tax) };
I haven't tried to compile it, but I think this is something along the lines of what you want.
The idea is that first you perform your join to link the customers and orders. Then apply your filter condition.
At that point, you want to get all the orders that have a particular group, so the group operator is applied.
Finally, order the results, then select out all info from the keys for each group, and sum up the tax in each of the group.
First of all, it would be nice to know what exactly you can't figure out. If you're completely lost and don't know where to begin then you need to google around for linq joining and grouping.
Here's something I did recently that may (possibly) point you in the right direction:
// This groups items by category and lists all category ids and names
from ct in Categories
join cst in Category_Subtypes
on ct.Category_Id equals cst.Category_Id
join st in Subtypes
on cst.Subtype_Id equals st.Subtype_Id
where
st.Type_Id == new Guid(id)
group ct by new { ct.Category_Id, ct.Display_Text } into ctg
select new
{
Id = ctg.Key.Category_Id,
Name = ctg.Key.Display_Text
}

Stuck on a subquery that is grouping, in Linq`

I have some Linq code and it's working fine. It's a query that has a subquery in the Where clause. This subquery is doing a groupby. Works great.
The problem is that I don't know how to grab one of the results from the subquery out of the subquery into the parent.
Frst, here's the code. After that, I'll expplain what piece of data i'm wanting to extract.
var results = (from a in db.tblProducts
where (from r in db.tblReviews
where r.IdUserModified == 1
group r by
new
{
r.tblAddress.IdProductCode_Alpha,
r.tblAddress.IdProductCode_Beta,
r.tblAddress.IdProductCode_Gamma
}
into productGroup
orderby productGroup.Count() descending
select
new
{
productGroup.Key.IdProductCode_Alpha,
productGroup.Key.IdProductCode_Beta,
productGroup.Key.IdProductCode_Gamma,
ReviewCount = productGroup.Count()
}).Take(3)
.Any(
r =>
r.IdProductCode_Alpha== a.IdProductCode_Alpha&&
r.IdProductCode_Beta== a.IdProductCode_Beta&&
r.IdProductCode_Gamma== a.IdProductCode_Gamma)
where a.ProductFirstName == ""
select new {a.IdProduct, a.FullName}).ToList();
Ok. I've changed some field and tables names to protect the innocent. :)
See this last line :-
select new {a.IdProduct, a.FullName}).ToList();
I wish to include in that the ReviewCount (from the subquery). I'm jus not sure how.
To help understand the problem, this is what the data looks like.
Sub Query
IdProductCode_Alpha = 1, IdProductCode_Beta = 2, IdProductCode_Gamma = 3, ReviewCount = 10
... row 2 ...
... row 3 ...
Parent Query
IdProduct = 69, FullName = 'Jon Skeet's Wonder Balm'
So the subquery grabs the actual data i need. The parent query determines the correct product, based on the subquery filters.
EDIT 1: Schema
tblProducts
IdProductCode
FullName
ProductFirstName
tblReviews (each product has zero to many reviews)
IdProduct
IdProductCode_Alpha (can be null)
IdProductCode_Beta (can be null)
IdProductCode_Gamma (can be null)
IdPerson
So i'm trying to find the top 3 products a person has done reviews on.
The linq works perfectly... except i just don't know how to include the COUNT in the parent query (ie. pull that result from the subquery).
Cheers :)
Got it myself. Take note of the double from at the start of the query, then the Any() being replaced by a Where() clause.
var results = (from a in db.tblProducts
from g in (
from r in db.tblReviews
where r.IdUserModified == 1
group r by
new
{
r.tblAddress.IdProductCode_Alpha,
r.tblAddress.IdProductCode_Beta,
r.tblAddress.IdProductCode_Gamma
}
into productGroup
orderby productGroup.Count() descending
select
new
{
productGroup.Key.IdProductCode_Alpha,
productGroup.Key.IdProductCode_Beta,
productGroup.Key.IdProductCode_Gamma,
ReviewCount = productGroup.Count()
})
.Take(3)
Where(g.IdProductCode_Alpha== a.IdProductCode_Alpha&&
g.IdProductCode_Beta== a.IdProductCode_Beta&&
g.IdProductCode_Gamma== a.IdProductCode_Gamma)
where a.ProductFirstName == ""
select new {a.IdProduct, a.FullName, g.ReviewCount}).ToList();
While I don't understand LINQ completely, but wouldn't the JOIN work?
I know my answer doesn't help but it looks like you need a JOIN with the inner table(?).
I agree with shahkalpesh, both about the schema and the join.
You should be able to refactor...
r => r.IdProductCode_Alpha == a.IdProductCode_Alpha &&
r.IdProductCode_Beta == a.IdProductCode_Beta &&
r.IdProductCode_Gamma == a.IdProductCode_Gamma
into an inner join with tblProducts.

Resources