counting items in grouped results in Linq to Sql - linq-to-sql

I want to group results of query by the item id and count number of items in each group. finally i will take only one item from each group and the count of that group to display
results.GroupBy(r=>r.ID)....

You can do some grouping in Linq like this:
var items = from r in rows
group r by r.ID into g
select new { Count = g.Count(), First = g.First() };
... but that'll give you a collection of objects with a "Count" property (int) and a "First" property (of the same type as your rows).
What you might want to do is select something that's shaped similarly to your row, except with a count property. One way to do that is field by field like this:
var items = from r in rows
group r by r.ID into g
let f = g.First()
select new
{
f.ID, f.Name, f.Foo, f.Bar, // etc
Count = g.Count()
};

Related

Disacrding multiple duplicate records from a left join

I have two tables:-
gallery
gallery_favorite
The user_id in gallery table means the user who posted the item. The user_id in gallery_favorite means the user who added the item in his favorite list. If favorite = 0, then it means the user had initially added the item in favorite list but later removed it.
Now, I want to fetch all the gallery items along with its favorite status. Here is my query:-
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
But a syou can see, I am getting duplicate records depending upon the number of rows wrt to a gallery item in the gallery favorite table. How can I modify the query to get only one record (along with my own favorite status)?
I guess you are getting duplicate records because you have not joined both the table on user_id -
Try below query -
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
and `gallery_favorite`.`user_id` = `gallery`.`user_id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
Assuming the gallery as many favorites from different users. It makes no sense to display exclusively a hit favorite to a user and gallery alone. Counting them makes sense.
Select `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
sum(gallery_favorite`.`favorite`) as total_favorites -- count them group function aggregate
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
and gallery_favorite`.`favorite` = 1 -- count only the favorites
GROUP BY `gallery`.`id` -- GROUP CLAUSE
This is how a join works. You get all rows matching the condition. You can either group the result by the fields in the left table (thus eliminating duplicates in the output) or join with a table that has one entry per gallery item -- this requires joining with a (SELECT ... FROM gallery_favorite GROUP BY gallery_id)

mysql select based on maths on values linked

I'm trying to output the top x items in my database based on a value of linked 'tags'
http://sqlfiddle.com/#!9/f23ad2
The function I'm currently using (note: :userId is the id from the user table):
SELECT
item.id,
item.name
FROM
item
LEFT JOIN
item_tag
ON
item.id = item_tag.item
LEFT JOIN
tag
ON
item_tag.tag = tag.id
LEFT JOIN
user_tag
ON
user_tag.user = :userId
AND
item_tag.tag = user_tag.tag
LEFT JOIN
item_tag_rating
ON
item_tag.tag = item_tag_rating.tag
AND
TRUNCATE(user_tag.val,0) = item_tag_rating.user_tag
ORDER BY
tag_rating DESC
LIMIT 10
The idea is that the user_tags table will tell you the user's tag_level (1-5) or if they don't currently have one use a default of 3 and based on that you get the value for that level in the item_tags table.
If there are 3 tags for instance you end up with 3 decimal numbers (percentage rating converted to decimal) which is then combined to get an overall percentage rating for the item. I then want to use that overall percentage number to output the top x items.
Any suggestions on how I could do this?

Join two tables using LINQ Query and order based two parameters

I have two tables Customers and Orders. I want a LINQ query to fetch list of all the orders placed by all the customers organized first by month and then by year. If there is no order corresponding to the customer, "No Orders” should be displayed.
The columns of Customers table are
customer_id
name
city
The columns of Orders table are
order_id
order_date
order_total
customer_id
I tried writing it the following way but its not giving complete output.
var res = from cust in db.Customers
join ord in db.Orders
on cust.customer_id equals ord.customer_id into g
from d in g.DefaultIfEmpty()
select new {
name=cust.name,
oId=d.order_id==null?-1:d.order_id
};
How do I rectify it?
I finally got the right answer which exactly the result as expected. I have put it below. I have used two LINQ queries to arrive at the result though. The first one gives the result, but the final result needs to be displayed with names of customer and their order totals, hence it is partial result. The second LINQ query further refines the 'partialResult' and gives the result as expected.
var partialResult = (from c in db.Customers
join o in db.Orders
on c.customer_id equals o.customer_id
select new
{c.name,
o.order_total,
o.order_date }).OrderBy(m => m.order_date.Month).ThenBy(y => y.order_date.Year);
var finalResult = from c in db.Customers
orderby c.name
select new
{
name = c.name,
list = (from r in partialResult where c.name == r.name select r.order_total).ToList()
};
foreach (var item in finalResult)
{
Console.WriteLine(item.name);
if (item.list.Count == 0)
{
Console.WriteLine("No orders");
}
else
{
foreach (var i in item.list)
{
Console.WriteLine(i);
}
}
}
Something like this. You can do it using LINQ Predicates
var res = Customers.Join(Orders, x => x.customer_id, y => y.customer_id, (x, y) => x).ToList();
This is what you can do:
var res = from cust in db.Customers
join ord in db.Orders
on cust.customer_id equals ord.customer_id into g
from d in g.DefaultIfEmpty()
orderby d.OrderDate.Year, d.OrderDate.Month
select new {
name=cust.name,
oId = d.order_id.ToString() ?? "No Orders"
};

LINQ - Is there a way to get element values after grouping without ForEach?

I have an Items table that contains a CategoryId field. This is an FK into a Categories table. I want to group my Items by CategoryId and return the Items and Categories.Name.
I know I can do this in C#:
var ItemsByCat =
from i in Items
group i by i.CategoryId into g
select g
foreach(var n in ItemsByCat)
{
blah, blah
}
My question is - is there a way to get at the element values part of the grouping without having to iterate through with a loop? Something like:
from i in Items
group i by i.CategoryId into g
select new {
CategoryID = g.Key,
CategoryName = ???,
Items = ???
}
Many thanks,
BK
I think this is what you're looking for:
from i in Items
group i by i.CategoryId into g
select new
{
CategoryID = g.Key,
CategoryName = g.First().Category.Name,
Items = g
}
EDIT: I hope this answers your question from the comments.
g is a collection of Items grouped by their CategoryId. Items in the select can be a projection including whatever subset of Item properties you require. Example:
from i in Items
group i by i.CategoryId into g
select new
{
CategoryID = g.Key,
CategoryName = g.First().Category.Name,
Items = g.Select(a => new{a.ItemID, a.ItemName})
}
What about joining the Categories table?
from i in Items
join c in Categories on i.CategoryId equals c.Id
group i by i.CategoryId into g
select new {
CategoryID = g.Key,
CategoryName = c.Name,
Items = i
}
CategoryName should be i.Category.CategoryName //the name property
given you have the relationship setup properly.
The items should be the values of group by, not sure why you'd need to do it again.
Assuming you have a collection of Categories in a variable categories it would look like this:
var ItemsByCategory =
from i in items
join c in categories on i.CategoryId equals c.Id
group i by i.CategoryId into g
select new {
CategoryId=g.Key,
CategoryName=c.Name,
Items=g.Items
}

SQL Query to Calculate Values based on Conditions

I have three relevant tables:
Evaluations:
EvalID
WorktypeID
PointsPossible
Items:
EvalID
ItemID
Value
QuestionID
Question_Worktype:
WorkTypeID
QuestionID
Points
There are multiple Items for each Evaluation, and each item has a QuestionID/WorkTypeID pair that relates to an entry in Question_Worktype, where the score lies.
Items.Value is whether an Item was correct or not.
I would like a query that returns the total Points of all Items for an Evaluation, Points only are awarded if the Value of the Item is 1.
My goal would be to get:
Eval_ID, Total Points (calculated), Points Possible
I can't seem to wrap my brain around it.
Edit: I forgot an important part:
I have an additional table:
qa_edits:
EditID
ItemID
NewValue
Date
If someone edits the value, it's stored here, and I really need it to calculate based on the newest edit for the item if there is one, or the item value if there isn't.
Start with your edit table, to get the latest edit for each item:
SELECT
M.ItemId,
COALESCE(Q.Value, I.Value) as Value,
I.QuestionId
FROM (
SELECT
I.ItemId,
MAX(Date) as LastEditDate
FROM Items as I
LEFT OUTER JOIN QA_Edits as E ON
I.ItemId = E.ItemId
GROUP BY
I.ItemId
) as M
LEFT OUTER JOIN QA_Edits as Q ON
M.ItemId = Q.ItemId
AND M.LastEditDate = Q.Date
LEFT OUTER JOIN Items as I ON
M.ItemId = Q.ItemId
AND M.LastEditDate IS NULL
I'd suggest a view for that - let's call it LatestItems.
Then, back to Blorgbeard's:
select
e.Eval_ID,
sum(qw.Points) as TotalPoints,
e.PointsPossible
from Evaluations e
join LatestItems i on
e.Eval_ID = i.Eval_ID
and i.Value = 1
join Questions_WorkType qw on
qw.QuestionID = i.QuestionID
group by
e.Eval_ID, e.PointsPossible
I think this should work:
select e.Eval_ID, sum(qw.Points) as TotalPoints, e.PointsPossible
from Evaluations e
left join Items i on e.Eval_ID = i.Eval_ID and i.Value = 1
join Questions_WorkType qw on qw.QuestionID = i.QuestionID
group by e.Eval_ID, e.PointsPossible