How can I use sum with group by in LINQ? - c#

I have the following many companies. Some of the companies have subjects and others do not. Something like this:
CompanyID Subjects
1 2
2 4
3 1
4 0
I am trying to create a LINQ report that will give me this information. This is what I have so far. It correctly does an outer join so that even companies with no subjects are include in the list. Once I have that data then I group the date by company title. The problem is that the last select does not work correctly. Can someone suggest how I can get the sum. I was able to use count() but I need a sum as the way I have set things up is that when there are no subjects a value of 0 goes into Subjects and where there is 1 a value of one goes there. So by summing the count of Subjects at each break in the group I should be able to find out how many subjects are assigned to the company.
var test1 = from c in companies
join s in subjects
on "0000" + c.RowKey equals s.PartitionKey into outer
from s in outer.DefaultIfEmpty()
select new
{
Title = c.Title,
Subjects = ((s == null) ? 0 : 1)
} into split
group split by split.Title into g
select new
{
Title = g.Key,
total = g.sum(s => s.Subjects)
};

You wrote the "sum" method without the capital "s". C# is case sensitive, so I think that's your problem, if you're getting a compile error.
Otherwise include the actual result of your query and what you expect, so we can compare it and try to find the problem.

Related

Get list of items where their ID is equal to some Values - linq c#

Here is a query:
from order in db.tblCustomerBuys
where selectedProducts.Contains(order.ProductID)
select order.CustomerID;
selectedProducts is a list containing some target products IDs, for example it is { 1, 2, 3}.
The query above will return customerIDs where they have bought one of the selectedProducts. for example if someone has bought product 1 or 2, its ID will be in result.
But I need to collect CustomerIDs where they have bought all of the products. for example if someone has bought product 1 AND 2 AND 3 then it will be in result.
How to edit this query?
the tblCustomerBuys are like this:
CustomerID - ID of Customer
ProductID - the product which the customer has bought
something like this:
CustomerID ProdcutID
---------------------------
110 1
110 2
112 3
112 3
115 5
Updated:
due to answers I should do grouping, for some reason I should use this type of query:
var ID = from order in db.tblCustomerBuys
group order by order.CustomerID into g
where (selectedProducts.All(selProdID => g.Select(order => order.ProductID).Contains(selProdID)))
select g.Key;
but it will give this error:
Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
The updated query is the general LINQ solution of the issue.
But since your query provider does not support mixing the in memory sequences with database tables inside the query (other than Contains which is translated to SQL IN (value_list)), you need an alternative equivalent approach of All method, which could be to count the (distinct) matches and compare to the selected items count.
If the { CustomerID, ProductID } combination is unique in tblCustomerBuys, then the query could be as follows:
var selectedCount = selectedProducts.Distinct().Count();
var customerIDs =
from order in db.tblCustomerBuys
group order by order.CustomerID into customerOrders
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Count() == selectedCount
select customerOrders.Key;
And if it's not unique, use the following criteria:
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Select(order => order.ProductID).Distinct().Count() == selectedCount
As your question is written, it is a bit difficult to understand your structure. If I have understood correctly, you have an enumerable selectedProducts, which contains several Ids. You also have an enumeration of order objects, which have two properties we care about, ProductId and CustomerId, which are integers.
In this case, this should do the job:
ver result = db.tblCustomerBuys.GroupBy(order => order.CustomerId)
.Where(group => !selectedProducts.Except(group).Any())
.Select(group => group.Key);
What we are doing here is we are grouping all the customers together by their CustomerId, so that we can treat each customer as a single value. Then we are treating group as a superset of selectedProducts, and using a a piece of linq trickery commonly used to check if one enumeration is a subset of another. We filter db.tblCustomerBuys based on that, and then select the CustomerId of each order that matches.
You can use Any condition of Linq.
Step 1 : Create list of int where all required product id is stored
Step 2: Use Any condition of linq to compare from that list
List<int> selectedProducts = new List<int>() { 1,2 } // This list will contain required product ID
db.tblCustomerBuys.where(o=> selectedProducts .Any(p => p == o.ProductID)).select (o=>o.order.CustomerID); // This will return all customerid who bought productID 1 or 2

SQL to LINQ troubles

I have some SQL which returns two columns, the X column and Y column:
SELECT TOP (100) PERCENT
dbo.SurveyAnswer.QuestionAnswer AS [Y],
COUNT(dbo.SurveyAnswer.QuestionAnswer) AS [X]
FROM
dbo.SurveyAnswer
INNER JOIN dbo.SurveyQuestion ON
dbo.SurveyAnswer.QuestionID = dbo.SurveyQuestion.QuestionID
INNER JOIN dbo.FieldAgentCall ON
dbo.SurveyAnswer.JobId = dbo.FieldAgentCall.JobId AND
dbo.SurveyAnswer.ObjectiveId = dbo.FieldAgentCall.ObjectiveID AND
dbo.SurveyAnswer.AgentId = dbo.FieldAgentCall.AgentID
INNER JOIN dbo.SurveyQuestionaire ON
dbo.FieldAgentCall.JobId = dbo.SurveyQuestionaire.JobId and
dbo.SurveyQuestion.QuestionaireID = dbo.SurveyQuestionaire.QuestionaireID and
WHERE
(dbo.SurveyQuestion.QuestionNo = 9) AND (dbo.SurveyQuestion.QuestionaireID = 1) AND
dbo.SurveyAnswer.QuestionAnswer <>'NA'
GROUP BY
dbo.SurveyAnswer.QuestionAnswer
ORDER BY
[Y]
The SQL searches through a range of tables and returns all the answers to a question and groups then, so the results would look similar to.
X | Y
No | 234
Yes | 43
The SQL works fine, I got that working without a problem, due to the length of the query and different parameters being sent in, the query got to an unmanageable size and decided it's time it became LINQ.
So I am trying to get the basic LINQ working to get results out, but being fairly new to LINQ, I can't quite get it working
var query = (from answers in db.SurveyAnswerModels.ToList()
join question in db.SurveyQuestion.Where(i => i.QuestionID == 9 && i.QuestionaireID == 1).ToList() on answers.QuestionID equals question.QuestionID
join questionnaire in db.SurveyQuestionnaire.ToList() on question.QuestionaireID equals questionnaire.QuestionaireID
join fieldagent in db.FieldAgentCall.ToList() on questionnaire.JobId equals fieldagent.JobId
group answers.QuestionAnswer by answers.QuestionAnswer into results
select new { X = results.Count(), Y = results });
The result I am getting for this is the wrong amount of counts for X and the Y data isn't group
[{"Xs":2814,"Ys":["No","No","No","No",
Though it's the wrong amount because I assume I've not added the right parameters yet, so that's something I can sort, the main problem I am having though is the group by, I tried to replicate it as much as possible but failed.
The "No's" should just be a "No" with the count of how many No's there are, which it's going with the counter as it says there are 2,814 No's, I just need it to only say say "No" once.
Any advice would be great too, like where I am going wrong.
Try this:
var query = (from answers in db.SurveyAnswerModels
join question in db.SurveyQuestion on answers.QuestionID equals question.QuestionID
join questionnaire in db.SurveyQuestionnaire on question.QuestionaireID equals questionnaire.QuestionaireID
join fieldagent in db.FieldAgentCall on questionnaire.JobId equals fieldagent.JobId
where question.QuestionID == 9 && question.QuestionaireID == 1
group answers.QuestionAnswer by answers.QuestionAnswer into results
select new { Count = results.Count(), Answer = results.Key });
Differences from yours:
The ToLists() are removed (this is at best unnecessary and at worst will screw up the C#-expression-to-SQL translation)
Moved the Where() down to the bottom (unnecessary, but makes it easier to follow)
Select results.Key as the answer. Key is the the "grouped by" value for reach result group.
I think 3. is possibly the only step necessary to get it working.

How to Get Top 5 Rated User in 2 Tables with LINQ using C#

I have 2 tables, one is Posts another is Comments. These tables contain "RatedPoint" field.
I want to take 5 users who have the highest point.
For example, user ID =1 and its total point 50 in Post table
and it's total point is 25 in Comment table, so its total point is 75
so, i have to look whole members and after choose 5 highest point
It seems a bit complicated, i hope its clear..
I tried something like that
var abc= csEntity.Users.Where(u => csEntity.Posts.Any(p => u.Id == p.UserId)).
Take(userCount).OrderByDescending(u => u.Posts.Count).ToList();
or..
var xyz = csEntity.Posts.Where(p => csEntity.Comments.Any(c => c.UserId == p.UserId));
I dont want to use 2 different list if possible.. is it possible to do it in one query?
I could do it with 2 for loops, but i think its a bad idea..
Post TABLE
Comments TABLE
As you see, these two tables contain userID and each user has RatedPoint...
I think now its clear
EDIT: Maybe a user never write a comment or never write a post just write a comment.. then i think we musnt make equal posts.userId=comments.UserId
Here is a LINQ expression that does what you seem to be asking for:
var result = from p in posts
join c in comments on p.Id equals c.Id
select new { Id = p.Id, Total = p.Points + c.Points };
That provides the actual joined data. Then you can pick the top 5 like this:
result.OrderByDescending(item => item.Total).Take(5)
Note that the above does assume that both tables always have each user, even if they didn't post or comment. I.e. they would simply have a point count of 0. Your updated question clarifies that in your case, you have potentially disjoint tables, i.e. a user can be in one table but not the other.
In that case, the following should work for you:
var leftOuter = from p in posts
join c in comments on p.Id equals c.Id into groupJoin
let c = groupJoin.SingleOrDefault()
select new { Id = p.Id, Total = p.Points + (c == null ? 0 : c.Points) };
var rightAnti = from c in comments
join p in posts on c.Id equals p.Id into groupJoin
let p = groupJoin.SingleOrDefault()
where p == null
select new { Id = c.Id, Total = c.Points };
var result = leftOuter.Concat(rightAnti);
The first LINQ expression does a left outer join. The second LINQ expression does a left anti-join (but I call it "right" because it's effectively the right-join of the original data :) ). I'm using SingleToDefault() to ensure that each user is in each table once at most. The code will throw an exception if it turns out they are present more than once (which otherwise would result in that user being represented in the final result more than once).
I admit, I don't know whether the above is the most efficient approach. I think it should be pretty close, since the joins should be optimized (in objects or SQL) and that's the most expensive part of the whole operation. But I make no promises regarding performance. :)

C# Linq eqiuvalent of SQL Count()

I have a fairly complicated join query that I use with my database. Upon running it I end up with results that contain an baseID and a bunch of other fields. I then want to take this baseID and determine how many times it occurs in a table like this:
TableToBeCounted (Many to Many)
{
baseID,
childID
}
How do I perform a linq query that still uses the query I already have and then JOINs the count() with the baseID?
Something like this in untested linq code:
from k in db.Kingdom
join p in db.Phylum on k.KingdomID equals p.KingdomID
where p.PhylumID == "Something"
join c in db.Class on p.PhylumID equals c.PhylumID
select new {c.ClassID, c.Name};
I then want to take that code and count how many orders are nested within each class. I then want to append a column using linq so that my final select looks like this:
select new {c.ClassID, c.Name, o.Count()}//Or something like that.
The entire example is based upon the Biological Classification system.
Update:
Assume for the example that I have multiple tables:
Kingdom
|--Phylum
|--Class
|--Order
Each Phylum has a Phylum ID and a Kingdom ID. Meaning that all phylum are a subset of a kingdom. All Orders are subsets of a Class ID. I want to count how many Orders below to each class.
I hope this is clear now.
Normally this is done with a group. For example:
from k in db.Kingdom
join p in db.Phylum on k.KingdomID equals p.KingdomID
where p.PhylumID == "Something"
join c in db.Class on p.PhylumID equals c.PhylumID
group c by new { c.ClassID, c.Name } into g
select new { Count = g.Count(), g.Key.ClassID, g.Key.Name };
That will basically count how many entries you have for each ClassID/Name pair. However, as Winston says in the comments, you're possibly interested in another table (Order) that you haven't told us about. We can't really give much more information until we know what you're doing here. Do you already have a relationship set up for this in LINQ to SQL? Please tell us about the Order table and how it relates to your other tables.
EDIT: Okay, with the modified question, I suspect we can ignore phylum and kingdom completely, unless I'm missing something. (I also can't see how this relates to a many-to-many mapping...)
I think this would work:
from o in db.Order
group o by o.ClassID into g
join c in db.Class on g.Key.ClassID equals c.ClassID
select new { c.ClassID, c.Name, g.Count() };

How to get All select keys in Linq Group By Clause

I am trying to get the rest of the select keys in this linq query but the intellisense is giving me an error
var query2 = from row2 in query1
group row2 by row2.questionid into g
where g.Count() > 0
select new
{
questionid1, //Error here
time, //Error here
thecount = g.Count()
};
How do I get those select keys?
I'm assuming that questionid and time are the properties you want to group on:
You can only get the keys you grouped on from g, and as Jon has suggested the where clause doesn't actually do anything.
Try this:
var query2 = from row2 in query1
group row2 by new { row2.questionid, row2.time } into g
// where g.Count() > 0
select new
{
g.Key.questionid,
g.Key.time,
thecount = g.Count()
};
It's not at all clear what you're asking, I'm afraid. What is "time"? What's the difference between questionid and questionid1?
By the time you've grouped, you've basically got a sequence of groups rather than a sequence of questions. When you project those groups, you can use the Key property of the group to get at the key which forms that group, and you can use the sequence of values within the group (which is what g.Count() is doing in your example). For example, you could take the first result within the group, and access individual fields within that.
If you could give more of an idea of what data you've got and what you're trying to achieve, we're much more likely to be able to help you.
As an additional note - your where clause isn't doing anything at the moment. When you group by a key, you never get any "empty" groups - LINQ isn't going to make up keys which aren't in the actual data.

Categories

Resources