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()
}
Related
I have two data tables that have results from queries made on two different servers. The items from the queries are related by a product ID, I am trying to combine the results into a new data table by joining on the product ids.
The basic structure of the tables are
//Product
product_id | field1 | field2 | field3...
//Inventory
id | fieldA | fieldB | fieldC...
and I want
product_id | field1 | field2 |field3 | fieldA | fieldB |fieldC
when product_id == id
This is pretty trivial to do in SQL but since the two data sets come from different databases on different servers I need to merge the results in my app for users.
The only semi-working solution I have is to get a left and right join from the tables, but I cannot figure out how to merge those results into a new data table. Here is some rough code of how I think it should be done. Any help on this would be appreciated as I am sure there is a much better way to do this.
var query = from S in Product.AsEnumerable()
join I in Inventory.AsEnumerable()
on S.Field<string>("product_id") equals I.Field<string>("id") into COLS
from entry in COLS
select entry;
var query2 = from I in Inventory.AsEnumerable()
join S in Product.AsEnumerable()
on I.Field<string>("id") equals S.Field<string>("product_id") into COLS2
from entry in COLS2
select entry;
var FullJoin = query.Union(query2);
if( FullJoin.Any())
{
Results = FullJoin.CopyToDataTable<DataRow>();
}
return Results;
Try the following code:
var query = from p1 in P1
join p2 in P2
on p1.ID equals p2.ProductID
select new { p1.ID, p1.Field1, p1.Field2, p1.Field3, p2.FieldA, p2.FieldB, p2.FieldC};
Let's say I have 3 tables - 1 header and 2 detail:
Header Table
id | label
1 | foo
2 | bar
Detail 1 Table
id | date | value
1 | 2015-01-01 | 5
Detail 2 Table
id | date | value
1 | 2015-01-01 | 7
2 | 2016-02-02 | 10
I want to make a linq query that joins all three, but does not eliminate data due to one detail table not having a record where the other one does. The result should look like:
Resulting Table
id | label | date | value1 | value2
1 | foo | 2015-01-01 | 5 | 7
2 | bar | 2016-02-02 | <null> | 10
So, a null for value1, instead of the entire row being removed.
If I were writing SQL, I could write
select
h.id,
h.label,
coalesce(d1.date, d2.date) as date,
d1.value as value1,
d2.value as value2
from
header h
left join detail1 d1
on d1.id = h.id
left join detail2 d2
on d2.id = h.id
and (
d2.date = d1.date
or d1.date is null
)
Is it possible to write this using Linq? I'm using the "on new equals new " syntax, and I cannot figure out how to preserve the detail2 record when there is no matching detail1 record.
Edit: I feel like the linked answer only answers the left join portion of my question. I know I can left join in linq, but the detail2 table is joining on to both header (not a problem) and detail1. If detail1 doesn't have a record for a date in detail2, the detail2 record will not appear in the result. Using "select new{} equals new{}" doesn't allow me to use the detail2 object before the equals, so I can't write
from
h in header.AsEnumerable()
join d1.AsEnumerable().DefaultIfEmpty()
on p.Id equals d1.Id
join d2.AsEnumerable().DefaultIfEmpty()
on new {
Id = h["Id"],
Date = d1["Date"] ?? d2["Date"], // Doesn't work, can't use d2 here.
} // d1 may not have a record, so there may not be a match
equals new {
Id = d2["Id"],
Date = d2["Date"],
}
select new {
// etc...
}
To implement a join with arbitrary conditions, you need to use another from clause with a where to handle your condition. I am not sure if used with Linq to SQL what type of SQL will be produced, you may be better off with my FullOuterJoin/LeftOuterJoin IQueryable extensions.
var ans = from h in header
join d1 in detail1 on h.id equals d1.id into hd1j
from hd1 in hd1j.DefaultIfEmpty()
from d2 in detail2 where h.id == d2.id && (hd1?.date == null || hd1.date == d2?.date)
select new { h.id, h.label, date = hd1?.date ?? d2?.date, value1 = hd1?.value, value2 = d2?.value };
For my Enumerable testing, I put in the conditional operators. You should remove them if testing against IQueryable (e.g. Linq to SQL).
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)
}
I have a table:
Group | BasalArea | SpeciesName
1 | 3.6 | Palustris
1 | 45.0 | MSO
2 | 4.2 | Oak
2 | 2.0 | MSO
...
From this table, I would like to get the species name with the highest basal area grouped by the Group field, which would look like this:
Group | BasalArea | SpeciesName
1 | 45.0 | MSO
2 | 4.2 | Oak
Using SQL, I can get the highest basal area:
SELECT Group, Max(BasalArea)
FROM TABLE
GROUP BY Group
I can't figure out how to also get the species name without doing some looping. Is this possible? What are the strategies for handling ties?
This is simpler in LINQ2SQL than in SQL:
var res = source.MyTable
.GroupBy(item => item.Group)
.Select(g => g.OrderByDescending(item => item.BasalArea).First())
.ToList();
This will return the list of items with largest values of BasalArea in its Group, together with SpeciesName.
In SQL you would need to join back to the original table, like this:
SELECT * FROM TABLE b
JOIN (
SELECT Group, Max(BasalArea) as BasalArea
FROM TABLE
GROUP BY Group
) t on t.Group = b.Group AND t.BasalArea = b.BasalArea
Try this:
var froup = categories.GroupBy(g => new {g.CategoryType})
.Select(g => g.OrderByDescending(i => i.CategoryID).First())
.ToArray();
What sasblinkenlight said would be the LINQ. Out of curiosity, here is a potential SQL solution.
SELECT grouped.Group, raw.SpeciesName, grouped.MaBasalArea
FROM (
SELECT Group, MAX(BasalArea) as MaxBasalArea
FROM TABLE
GROUP BY Group
) grouped
INNER JOIN TABLE raw ON grouped.MaxBasalArea = raw.BasalArea AND grouped.Group = raw.Group
Using Linq to Sql how do i group the following 2 tables.
Orders Table:
CustomerID | Name |Date
1 | order1 | 2010-01-01
2 | order2 | 2010-01-01
2 | order3 | 2010-04-01
Calls Table:
CustomerID | Name |Date
1 | call1 | 2010-01-01
3 | call2 | 2010-06-01
2 | call3 | 2010-05-01
I want to group the two tables by date , Result:
Date | Orders | Calls
2010-01-01 | 2 | 1
2010-04-01 | 1 | 0
2010-05-01 | 0 | 1
2010-06-01 | 0 | 1
i know how to group a single table ,
from o in Orders
group o by o.Date.Date into og
select new {Date = og.Key,Orders= og.Count()};
how do i group both?
thx!
Since both tables seem to have a similar structure I'd recommend projecting both into an equivalent form and then group on the concatenation of those two sets.
var orders = from o in Orders
select new { IsOrder = true, o.Date };
var calls = from c in Calls
select new { IsOrder = false, c.Date };
var result = from x in orders.Concat(calls)
group x by x.Date into og
select new {Date = og.Key, Orders= og.Count(o=>o.IsOrder), Calls = og.Count(c=>!c.IsTrue)};
Due to the lazy nature of Linq2Sql this might actually be reduced to a single query. In the interest of performance I would make sure this is not a query from hell.
You can use the Union method:
var result =
(from c in Calls group c by c.Date into cg select new {Date = cg.Key, Calls = cg.Count(), Orders = 0})
.Union(from o in Orders group o by o.Date into og select new {Date = og.Key, Calls = 0, Orders = og.Count()})
.GroupBy(x => x.Date)
.Select(g => new {Date = g.Key, Calls = g.Max(r => r.Calls), Orders = g.Max(r => r.Orders)});
foreach (var row in result)
{
Trace.WriteLine(row);
}
This is very similar to the SQL you would write (a union of the two tables, and then an outer query to merge the results into a row)