Linq group by and sum with multiple tables - c#

I have two tables Orders and OrderDetails and i am trying to group by the username which is found in the orders table and then a sum of the quantities which is found in order details
Orders table
|Order ID |Username|
|1 |User 1 |
|2 |User 1 |
|3 |User 2 |
OrderDetails Table
|OrderDetailsID| Qty| Order ID|
|1 | 50 |1 |
|2 | 20 |1 |
|3 | 30 |2 |
|4 | 20 |3 |
How could i get the total quantity grouped by user using Linq?
This is what i came up with
from order in Entity.Orders
join od in Entity.OrderDetails on order.ID equals od.OrderID
where order.Status != 2
group order by new { order.Username } into users
select new StatsiticsViewClients()
{
Username = users.Key.Username,
Qty = users.SelectMany(x => x.OrderDetails).Sum(x => x.Qty)
}

You should group od instead of grouping order, because OrderDetails contains the property you're trying to sum.
And you don't need to use anonymous type within the grouping, because you group using only one column.
from order in Entity.Orders
join od in Entity.OrderDetails on order.ID equals od.OrderID
where order.Status != 2
group od by order.Username into users
select new StatsiticsViewClients()
{
Username = users.Key,
Qty = users.Sum(g => g.Quantity)
}

Group OrderDetails and then you can get the Sum using g.Sum(x=> x.Qty) as well as Username from the Group Key field
from order in Entity.Orders
join od in Entity.OrderDetails
on order.ID equals od.OrderID
where order.Status != 2
group OrderDetails by order.Username into g
select new StatsiticsViewClients()
{
Username = g.Key.Username,
Qty = g.Sum(x=> x.Qty)
}

Related

LINQ Left Join and GroupBy

Let say I have this SQL Query:
SELECT
PromotionSlot.StaffFName,
SUM(PromotionSlot.Max_Occupancy) AS TOTAL,
SUM(DISTINCT(Booking.Quantity)) AS Occupied
From PromotionSlot
LEFT JOIN Booking
ON PromotionSlot.StaffID=Booking.StaffID
GROUP BY PromotionSlot.StaffFName
Result:
|StaffFName |TOTAL |Occupied|
-----------------------------------
|Jason |13 |1 |
|John Doe |9 |0 |
|Marry Jane |7 |2 |
This is my DB Table:
PromotionSlot TABLE: ID(PK),Max_Occupancy,StaffFName..., StaffID(FK)
Booking TABLE: ID(PK), Quantity...,StaffID(FK)
How can I translate it into LINQ? This is my attempt:
var staffData =
(from ps in dc.PromotionSlots
join b in dc.Bookings on ps.StaffID = b.StaffID
group ps by ps.StaffFName into NewGroup
select new dataView
{
StaffFName = NewGroup.Key,
Total = NewGroup.Sum(a => a.Max_Occupancy),
//problem:
//Occupied = NewGroup.Sum(b => b.Quantity)
}
Plan to have Occupied = NewGroup.Sum(b => b.Quantity) but when I try to point the b to the quantity column from Booking table it shows error(red-line) and I think the problems comes from group ps by ps.StaffFName into NewGroup makes it available for PromotionSlot table instead of Booking table. But I totally have no idea how to solve this!
Based on your SQL query, what you need is take Distinct quanties and Sum them.
var staffData =
(from ps in dc.PromotionSlots
join b in dc.Bookings on ps.StaffID = b.StaffID into slots
from slot in slots.DefaultIfEmpty()
group new {ps, slot} by ps.StaffFName into NewGroup
select new dataView
{
StaffFName = NewGroup.Key,
Total = NewGroup.Sum(a => a.ps!=null? a.ps.Max_Occupancy: 0),
//problem:
Occupied = NewGroup.Select(x=>x.slot.Quantity).Distinct().Sum()
}

How to join one row to every row in source table using LINQ

I have the following table (table A):
ID | Data |
1 | Data1 |
2 | Data2 |
3 | Data3 |
4 | Data4 |
I have table B that has 1 row:
DummyID | Dummy |
1 | Dummy1 |
I have to join table A with table B in the following way:
ID | Data |DummyID | Dummy |
1 | Data1 |1 | Dummy1 |
2 | Data2 |1 | Dummy1 |
3 | Data3 |1 | Dummy1 |
4 | Data4 |1 | Dummy1 |
Obviously I can't use any ID in the on clause.
from item in context.TableA
join dummy in context.TableB on ? = ?
select new
{
RowA=item,
Dummy=dummy
}
How could I do that with LINQ?
That's a cross join which you can get via Linq in the following way
from item in context.TableA
from dummy in context.TableB
select new
{
RowA=item,
Dummy=dummy
}
Or the following in method syntax
context.TableA.SelectMany(
item => context.TableB.Select(dummy => new { RowA = item, Dummy = dummy }));
Note that if TableB every has more than one row you'll end up with N times M rows where N is the number of rows in TableA and M is the number of rows in TableB.
No need to join at all.
from item in context.TableA
select new
{
RowA = item,
Dummy = context.TableB.FirstOrDefault()
}
Having said that, I'd have to question why you're doing this. The idea of LINQ is to get your relational data into an object-oriented form. Why not just retrieve the TableB information once and do whatever processing you need to do in-memory? It would reduce the size of the payload you're transferring from the database back to the application.
Why do you want to use join. You want to use each item in your old sequence to create a different item in your new sequence. In LINQ you would use Enumerable.Select for this:
var dummy = context.Dummy.FirstOrDefault();
var newSequence = context.TableA
.Select(itemInTableA =>
new
{
RowA = itemInTableA,
Dummy = dummy,
});

Linq Left Outer Join With newest datetime in right side

I have "Orders" table where its primary key is "OrderId ":
OrderId | OrderName
------- | ----------
1 | Order X
2 | Order Y
3 | Order Z
and "OrderDetails" table where its primary key is "OrderDetailsId " foreign key is 'OrderId":
OrderDetailsId | OrderId | ItemId | DeliveryDate
-------------- | ------- | ------ | ------------
10 | 1 | AA | 1/1/2010
20 | 1 | BB | 1/1/2013
30 | 2 | CC | 1/1/2012
40 | 2 | CC | 1/1/2014
Each order has ZERO or more order details, each order detail has specific delivery date.
We want to get all the orders, whether they have order details or not, and mark just one order as VIP if it has the order detail that has the maximum "delivery date"
This is the expected output:
OrderId | OrderName | IsVIP
------- | --------- | -----
1 | Order X | NO
2 | Order Y | YES
3 | Order Z | NO (since it has no order details)
That's because the maximum delivery date is for OrderDetailsId = 40 which belongs to OrderId = 2
How to accomplish this using the most readable LINQ code
I am not sure if you have OrderDetails property in orders collection (if so then #juharr's answer is correct). But, if they are not then you can make use of group join like this:-
var result = from o in orders
join od in orderDetails
on o.OrderId equals od.OrderId into g
select new {
OrderId = o.OrderId,
OrderName = o.OrderName,
IsVIP = g.Any(x => x.DeliveryDate == orderDetails.Max(z => z.DeliveryDate))
? "Yes" : "No"
};
Here is an example Fiddle with linq-to-objects.
Use navigation properties. Note this will set IsVIP to "YES" for all orders that contain an order detail with the max delivery date.
var query = from order in db.Orders
select new
{
order.OrderId,
order.Name,
IsVIP = order.OrderDetails.Any(
od => od.DeliveryDate == db.OrderDetails.Max(x => x.DeliveryDate))
? "YES"
: "NO"
};

Get first row of join

I have a linq query that joins two tables (no relation in the actual db)
the relation is:
Companies 1 - n Phones
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).ToList().Distinct();
this returns something like:
----------------------------
company1 | 12345 |
----------------------------
company1 | 23456 |
----------------------------
company2 | 43242 |
----------------------------
company2 | 34234 |
----------------------------
company2 | 65442 |
----------------------------
i need to get only the fisrt in Phones table not everything
how to do that?
Edit:maybe i wasn't clear about what want sorry for that.
i ment:
----------------------------
company1 | 12345 |
----------------------------
company2 | 43242 |
----------------------------
i want the first phone for each company
You can use GroupBy:
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).GroupBy(c=>c.Name).Select(c=>c.FirstOrDefault()).ToArray();
1 You can try with First operator
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
phones.Phone,
}).First();
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/system.linq.queryable.first.aspx
2 You can also use FirstOrDefault
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/system.linq.queryable.firstordefault.aspx
3 You can also use Take(1);
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/bb300906.aspx
I would use the following:
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).ToList().Distinct().FirstOrDefault();
The 'FirstOrDefault()' method will return the first item in the collection. If the collection contains no elements, the default object will be returned. For nullable types, this will be a 'null' object. This is a good way to prevent your application from failing due to an exception related to an empty collection.

Get distinct columns with order by date

I have these following two tables:
Job Title | PostDate | CompanyId
Assitant | 12/15/10 | 10
Manager | 12/1/10 | 11
Developer | 12/31/10 | 10
Assitant | 12/1/10 | 13
PM | 11/29/10 | 12
CompanyId | Name
10 | Google
11 | Yahoo
12 | Microsoft
13 | Oracle
Now i would like to get 3 different companies with the jobs sorted by post date. The result table would be following:
Job Title | PostDate | CompanyName
Developer | 12/31/10 | Google
Manager | 12/1/10 | Yahoo
Assitant | 12/1/10 | Oracle
How can I achieve that using a linq query? Any help will be appreciated...
I think that would be something like:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
group job by company into jobsByCompany
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.First()
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = jobsByCompany.Key.Name
};
It's a little odd to do a join and then a group - we could do a GroupJoin instead, but then discard empty options:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
into jobsByCompany // Make this a group join
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.FirstOrDefault()
where lastJob != null
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = company.Name
};
EDIT: Note that doesn't just take the top three results. Use query = query.Take(3); to just get the first three results.

Categories