How do I convert this statement into linq? - c#

How can I use Sum() in Linq?
select sum(op.Quantity),
sum(p.UnitPrice),
sum(p.Discount),
o.CreateAt
from OrderProduct op join
[Order] o on o.ID = op.OrderID join
Product p on p.ID = op.ProductID
group by o.CreateAt

Instead of just writing a SQL statement, it would be more helpful to define your goal. You have some tables and you want to extract information from it. Which information do you want? Now I have to guess your structure.
It seems you have Orders and Products and OrderProducts.
Every Order has zero or more OrderProducts; every OrderProduct belongs to exactly one Order. Every Order also has a CreateAt property.
Every Product has zero or more OderProducts; every OrderProduct belongs to exactly one Product. Every Product also has a UnitPrice and a Discount.
Finally every OrderProduct has a property Quantity
You didn't mention whether you are using entity framework or not. But even if not, you have access to the three tables using IQueryables. In Entity Framework this is done via the DbSets:
IQueryable<Product> products = ...
IQueryable<Order> orders = ...
IQueryable<OrderProduct> orderProducts = ...
The query in baby steps:
var joinedItems = from product in Products
join orderProduct in orderProducts on oderProduct.ProductId == product.Id
join order in orders on order.Id == orderProduct,OrderId
select new
{
Quantity = orderProduct.Quantity,
UnitPrice = product.UnitPrice,
Discount = product.Discount,
CreateAt = order.CreateAt,
};
Multiple joins is the only time I use query syntax instead of method syntax.
Click here to see the method syntax for multiple joins
Because every product has only one UnitPrice, I assume you don't want the sum of all UnitPrice of one Product, but you want the sum of all Unitprices of items created on CreateAt, and similarly the sum of all Discounts and Quantities.
So we first group all joinedItems into joinedItems with the same CreateAt:
var itemsGroupedByCreateAt = joinedItems
.GroupBy(joinedItem => joinedItem.CreateAt);
In words: take all joinedItems and group them into groups where all joinedItems in the group have the same value for CreateAt.
The result is a sequence of Groups. Every group has joinedItems. All joinedItems in the group have the same value for CreateAt. this common value is the Key of the group.
So all you have to do is for every group to sum the UnitPrices of all joinedItems in the group. Do the same for the Quantity and Discount. Note that every group will create one sumResult:
var sumResults = itemsGroupedByCreateAt
.Select(group => new
{
TotalQuantity = group
.Select(groupElement => groupElement.Quantity)
.Sum(),
TotalUnitPrice = group
.Select(groupElement => groupElement.UnitPrice)
.Sum(),
Totaldiscount = group
.Select(groupElement => groupElement.UnitPrice)
.Sum(),
// only if you need it in your result:
CreateAt = group.Key,
});
In words: take all your groups of joinedItems. From every group, take the joinedItems in the group. From these joinedItems take only the Quantities, and Sum them. Put the result in totalQuantity.
Do the same for the UnitPrices and the Discounts
Of course you can do this all in one big linq statement. Not sure whether this would improve readability and maintainability.

Related

Linq Find the most recurring record

I have a rent a car project.
This is the table where I keep the rented vehicles
I want to find the most rented vehicle in this table. How can I do this?
So I want to find the most mentioned car Id in the table
[I have to do it with context architecture. How can I do this with c # linq?]2
I want to find the most rented vehicle in this table.
So basically you want to find out how many times car 5 is rented, and how many times car 6 is rented, etc, and keep the carId with the highest rental count.
As you are working with a database, my advice would be to use GroupBy to make groups of rentals that have the same CarId, and then keep the group that has the most number of rentals = order by descending count and take the first.
We'll use the overload of Queryable.GroupBy that has a parameter resultSelector, so we can select the number of rentals in the group as result.
// make groups of CarRentals that have the same CarId:
var mostRentedCarId = dbContext.CarRentals.GroupBy(carRental => carRental.CarId,
// parameter resultSelector: for every CarId and all CarRentals that have this CarId,
// make one new object
(carId, carRentalsWithThisCarId) => new
{
CarId = carId,
RentalCount = carRentalsWithThisCarId.Count(),
})
// order the groupBy result by descending rentalCount and take the first or default:
.OrderByDescending(groupByResult => groupByResult.RentalCount)
.FirstOrDefault();
if you mean that you want to find the sql query string for that question u can use the count() to count how many times the id of a specific car is repeated. And since you want the most rented car you can use somthing like this:select top 1 count(CarId) from tableName group by CarId order by count(CarId) desc
this should work for you.
You could write a query like the below, or use the approach as suggested by Abdelghafour Lahrache.
SELECT MAX(CarCount)
FROM
(
SELECT COUNT(CarId) AS CarCount
FROM YourTableName
GROUP BY CarId
) CarIdCounter

Get TOP5 records from each status using Linq C#

I have a VacancyApply table and that table consist of Status Id's,So i need Top5 data from each Status.I want to get top 5 records of each status.Status is int like 1,2,3
My Query
var result = (from ui in _context.VacancyApply
join s in _context.UserProfile on ui.UserId equals s.UserId
join x in _context.Vacancy on ui.VacancyId equals x.VacancyId
join st in _context.Status on ui.StatusId equals st.StatusId
where ui.UserId == userId && ui.IsActive == true
orderby ui.StatusId
select new VacancyApply
{
VacancyApplyId = ui.VacancyApplyId,
VacancyId = ui.VacancyId,
UserId = ui.UserId,
StatusId = ui.StatusId,
VacancyName = x.VacancyName,
VacancyStack = x.VacancyStack,
VacancyEndDate = x.VacancyEndDate,
StatusName = st.StatusName,
UserName = s.FirstName
}).ToList();
Now what I can see from the output is that it contains One VacancyId and One VendorId.
I have a feeling that you have Many to Many relationships between Vacancy and Status tables.
But nevertheless, the answer is very simple: you need to use LINQ Take extension method (maybe it will be good to make it follow after the OrderBy because just taking the last items doesn't make sense without some logic):
var output = (logic to join, filter, etc.).OrderBy(lambda).Take(N); // N is the number of
// items you want to select
Now if you want Generally to take the last items from Vacancy and only after join it with Status do this:
var output = Vacancy.OrderBy(lambda).Take(N).(now join, filter, etc. with other tables);
However, if you want to Group all similar Statuses in conjunction with Vacancies and only after taking the Top items, use GroupBy:
var output = (logic to join, filter, etc.).GroupBy(st => st.StausId).
.Select(group => group.OrderBy(lambda).Take(N));

Simple linq query using group in order to return a one to many relationship

I have a data table of users and another table of user_ratings. It is a one to many relationship so the they are joined by UserId, the users table being the primary key and the user_ratings having the foreign key relationship. Each user_ratings has a column called rating_value and its filled with an int, negative or positive, and it gets summed together to be calculated as the user's rating. I want to be able to pull ratings for each user depending on the date for given ratings. Here is what I have so far with my linq statement, but I don't think it is right.
var profiles = from userprofs in Ent.UserProfiles
join userratings in Ent.UserRatings on userprofs.UserId equals userratings.UserId
where userratings.DateAdded >= drm.Start
group userprofs by userprofs.UserId into g
select new { ... };
Not really sure where to go from here, I'm not sure if I'm using the group correctly. I want to be able to iterate through this collection later to be able to display each user and his or her associated rating based of the sum of the user_ratings
If all the information you wish to know about the ratings is aggregate information (sum, average, count, etc) then you can do the likes of this:
var profiles = from userprof in Ent.UserProfiles
join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
where userrating.DateAdded >= drm.Start
group userrating by userprof into g
select new {g.Key.UserID, g.Key.UserName, Count = g.Count(), Avg = g.Avg(r => r.RatingValue) };
Note that I changed the plurals to singular names, since we define linq queries in terms of each individual item (UserProfiles is plural, and userprof each item "in" it).
This has a nice straight-forward conversion to SQL:
SELECT up.userID, up.userName, COUNT(ur.*), AVG(ur.ratingValue)
FROM UserProfiles up JOIN UserRatings ur
ON up.userID = ur.userID
WHERE dateAdded > {whatever drm.Start is}
GROUP BY up.userID, up.userName
If we want to be able to get into individual ratings, this does not match too well with a single SQL query, we could do:
var profiles = from userprof in Ent.UserProfiles
join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
where userrating.DateAdded >= drm.Start
group new{userrating.RatingValue, userrating.SomeThing} by new{userprof.UserID, userprof.UserName};
This would give us an IEnumerable<Grouping<{anonymous object}, {anonymous object}>> that we could iterate through, getting a new Key.UserID and Key.UserName on each iteration, and being able to iterate through that in turn getting item.RatingValue and item.SomeThing (that I added in for demonstration, if we want just the value it would be an IEnumerable<Grouping<{anonymous object}, int>>) like this:
foreach(var group in profiles)
{
Console.WriteLine("The user is :" + group.Key.UserName + " (id: " + group.Key.UserID ")");
foreach(var item in group)
{
Console.WriteLine(item.SomeThing + " was rated " + item.RatingValue);
}
}
However, the problem with this, is that there isn't a nice single SQL query that this maps to. Linq will do its best, but that'll mean executing several queries, so you're better off helping it out:
var profiles = from item in (from userprof in Ent.UserProfiles
join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
where userrating.DateAdded >= drm.Start
select new{userrating.RatingValue, userrating.SomeThing, userprof.UserID, userprof.UserName}).AsEnumerable()
group new{item.RatingValue, item.SomeThing} by new{item.UserID, item.UserName}
This has the same output as before, but the translation into SQL allows for a single query to be made, with the rest of the work being done in memory. 99% of the time, dragging some work into memory like this makes things less efficient, but because the previous doesn't have a single SQL query it can map to, it's an exception.
If I understand your question:
from userprofs in Ent.UserProfiles
join userratings in Ent.UserRatings on userprofs.UserId equals userratings.UserId
where userratings.DateAdded >= drm.Start
group new{userprofs,userratings} by userprofs.UserId into g
select new { userId=g.Key, userRating=g.Sum(i=>i.userratings.rating_value) };
GroupBy returns an IEnumerable of IGrouping. An IGrouping is basically an IEnumerable with an extra Key property that contains the property that was used to make the grouping. If you Sum the rating_value of the grouping, you're there.

LINQ: Group by aggregate but still get information from the most recent row?

Let's say I have a table that holds shipping history. I'd like to write a query that counts the amount of shipments per user and gets the shipping name from the most recent entry in the table for that user.
Table structure for simplicity:
ShipmentID
MemberID
ShippingName
ShippingDate
How do I write a LINQ C# query to do this?
It sounds like might want something like:
var query = from shipment in context.ShippingHistory
group shipment by shipment.MemberID into g
select new { Count = g.Count(),
MemberID = g.Key,
MostRecentName = g.OrderByDescending(x => x.ShipmentDate)
.First()
.ShipmentName };
Not really a LINQ answer, but personally, I'd be dropping to SQL for that, to make sure it isn't doing any N+1 etc; for example:
select s1.MemberID, COUNT(1) as [Count],
(select top 1 ShippingName from Shipping s2 where s2.MemberID = s1.MemberID
order by s2.ShippingDate desc) as [LastShippingName]
from Shipping s1
group by s1.MemberID
You can probably do LINQ something like (untested):
var qry = from row in data
group row by row.MemberId into grp
select new {
MemberId = grp.Key,
Count = grp.Count(),
LastShippingName =
grp.OrderByDescending(x => x.ShippingDate).First().ShippingName
};

Problem with LINQ using the values of a table before a GROUP BY

I have the following GROUP BY:
var stocks =
from p in products
from w in p.Warehouses
from l in w.locations
group l by l.Date into g
let maxDate = g.Max(l1 => l1.Date)
select new { Product=p.Name, Location= g.Where(l2 => l2.Date == maxDate) };
But it isn't working, i think because I am doing a group by I am not allowed to use values from the tables before the group by in my select.
Here is the hierarchy of the objects.
Products
Each product has multiple warehouses
Each warehouse has multiple locations.
I need to return all products and each product must contain the location name.
What is the criteria to find the location when there are multiple warehouses and multiple locations.
I must search in each warehouse and in turn each location... and return OUT OF ALL OF THEM the latest location (ONLY 1) which I detect using the Date == maxDate.
But it won't let me select the "Name" that was in p. It's not in g as g is group by of "locations".
I think you want something like this:
var query = from product in products
let location = (from warehouse in p.Warehouses
from location in p.Locations
orderby location.Date descending
select location).FirstOrDefault()
select new { Product = product.Name,
Location = location };
Note that Location may be null, if there are no matching locations.
There's no need to use a let clause here, but I figured this would be the most readable way of writing the query.
Now you don't really need to sort all the locations, of course... but there's no MaxBy in LINQ, which is what you really want. If you're using LINQ to Objects you could write your own MaxBy method (and there's one in MoreLINQ); in LINQ to SQL etc I'd expect the database to optimize the query anyway.

Categories