Order by and group by and sum using SQL - c#

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram

Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);

Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

Related

Lambda left join with rows

I have two tables. A table called Order and a table called OrderRows.
An Order can have zero or more OrderRows.
I want to query all Orders and do a Sum for all OrderRows that belong to that Order.
I do that like this:
var model = await _dbContext.Orders
.Join(_dbContext.OrderRows, o => o.Id, or => or.OrderId, (o, or) => new {o, or})
.GroupBy(x => new
{
x.o.Id,
x.o.Name
})
.Select(g => new CustomDto
{
Id = g.Key.Id,
Name = g.Key.Name,
TotalPrice = g.Sum(x => x.wkr.Price)
}).ToListAsync();
This works fine for all Orders that have OrderRows. However, some Orders don't have any OrderRows (yet).
Right now the Orders that don't have any OrderRows, are not included in the result.
In those cases I still want to have them in my result, but with a TotalPrice of 0.
What do I have to change in my Lambda query?
You can use simple Select without grouping. Just calculate TotalPrice as sub-query:
var model = await _dbContext.Orders.Select(o => new CustomDto
{
Id = o.Id,
Name = o.Name,
TotalPrice = _dbContext.OrderRows.Where(or => or.OrderId == o.Id).Sum(or => or.wkr.Price)
}).ToListAsync();
I've not tested it, but hope that idea is clear

How to make 2 dictionary and group it twice

I am calculating totals per month for each country. I have managed to group data by country, but I get error
An item with the same key has already been added.
when trying to put monthly totals into inner dictionary:
var totalPerMonth = data.AsEnumerable()
.Select(x => new
{
Date = Convert.ToDateTime(x.ItemArray[0]).ToString("yyyy-MM"),
Country = x.ItemArray[1],
Revenue = x.ItemArray[2]
})
.GroupBy(x => x.Country)
.ToDictionary(x => x.Key, x => x.ToDictionary(p => p.Date,////this is not unique/// p => Convert.ToDouble(p.Revenue)));
how to group it to make Date key unique?
You can either use ToLookup instead of ToDictionary to allow several values for same date.
Or you can use grouping to get unique dates only (assume you want to calculate totals for each month, so use Sum of revenue for each date group dg ):
var totalPerMonth = data.AsEnumerable()
.Select(x => new {
Date = Convert.ToDateTime(x.ItemArray[0]).ToString("yyyy-MM"),
Country = x.ItemArray[1],
Revenue = Convert.ToDouble(x.ItemArray[2]) // convert here
})
.GroupBy(x => x.Country)
.ToDictionary(
g => g.Key,
g => g.GroupBy(x => x.Date).ToDictionary(dg => dg.Key, dg => dg.Sum(x => x.Revenue))
);

Group by generates a huge query

I have the following linq query that produces a very big SQL.
var visits = _db.Visits.AsNoTracking().GroupBy(x => x.City)
.Select(group => new
{
City = group.Key.Code,
CityName = group.Key.Name,
Count = group.Count()
}).OrderByDescending(x => x.Count);
Because the Visits table has lots of columns. But I am interested in just one column in that Visits table, which I am grouping by.
So this hits performance and query is slow.
How can i make it faster?
Select out just the data you need first so the data set you are working with will be smaller. This should reduce the amount of columns and the size of the query.
var visits = _db.Visits.AsNoTracking()
.Select(c=> new // reduce the initial data set
{
City= c.City,
Code = c.Code,
Name = c.Name
})
.GroupBy(x => x.City)
.Select(group => new // build results
{
City = group.Key.Code,
CityName = group.Key.Name,
Count = group.Count()
})
.OrderByDescending(x => x.Count);

Find MAX/MIN list item using LINQ?

I have a list Having multiple Items and 3 props ID,DATE,COMMENT.ID field is Auto incremented in DATABASE.
Let say list Contains
2,16AUG,CommentMODIFIED
1,15AUG,CommentFIRST
3,18AUG,CommentLASTModified
I want to get a single ITEM.Item Having Minimum DATE and having Latest Comment. In this case
1,15AUG,CommentLASTModified
Any easy way to do it using LINQ.
orderedItems = items.OrderBy(x => x.Date);
var result = items.First();
result.Comment = items.Last().Comment;
To get a single item out of the list, you can order the items then take the first one, like this:
var result = items
.OrderByDescending(x => x.Date)
.First();
But First will throw an exception if the items collection is empty. This is a bit safer:
var result = items
.OrderByDescending(x => x.Date)
.FirstOrDefault();
To get the min / max of different columns you can do this:
var result =
new Item {
Id = 1,
Date = items.Min(x => x.Date),
Comment = items.Max(x => x.Comment)
};
But this will require two trips to the database. This might be a bit more efficient:
var result =
(from x in items
group x by 1 into g
select new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();
Or in fluent syntax:
var result = items
.GroupBy(x => 1)
.Select(g => new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories