Converting Inner Join Grouping SQL into Linq - sql

I have the following SQL statement:
SELECT b.Brand_key, b.BrandName, Sum(po.Quantity) AS Quantity
FROM Brands b
INNER JOIN Products p ON b.Brand_key = p.Brand_fkey
INNER JOIN ProductOrders po ON p.Product_key = po.Product_fkey
GROUP BY b.Brand_key
ORDER BY Quantity DESC, b.BrandName ASC
which works perfectly. Distinct values are returned as expected. I've attempted to turn it into Linq, but I feel like I'm going down a rabbit hole that never ends.
var testModelTwo =
(from b in db.Brands
join p in db.Products on b.brandKey equals p.brandKey
select new { brandKey = b.brandKey, BrandName = b.BrandName, ProductKey = p.productKey }
into groupResultBP
join po in pom on groupResultBP.ProductKey equals po.Product_fkey
select new
{
brandKey = groupResultBP.brandKey,
BrandName = groupResultBP.BrandName,
Quantity = po.Quantity
} into gr
group gr by gr.brandKey into gd
select new { Brand = gd.Key, BrandCounter = gd.Sum(c => c.Quantity) } into sumGroup
select sumGroup).ToList();
This seems to get to the point where I have a key and the sum, but need to do another join to get back the brand item information with sumGroup.
I feel like I've gone round the houses about 100 times and this is much easier than I'm making it! Is what I've done so far correct?

Related

How to convert this SQL into a LINQ QUERY

I am having trouble turning the following sql code into LINQ. I have it mostly working but I can't figure out how to get the revision from the items table which is gotten in those subqueries. Any help would greatly be appreciated.
Here is the SQL query:
SELECT
dbo.SerialOptCon.GROUPID, TopRevsion.RevName AS NAME,
dbo.SerailOptionNo.PartNo, dbo.SerialOptCon.PRICE,
0.00 AS DISCOUNT,
dbo.GroupConfig.CURRENCY,
'OPTION' AS TYPE, dbo.SerialModelGroupOptions.MODEL,
dbo.SerialModelGroupOptions.SEQUENCE
FROM
dbo.SerialModelGroupOptions
INNER JOIN
dbo.SerialOptCon
INNER JOIN
dbo.SerailOptionNo ON dbo.SerialOptCon.OptionId = dbo.SerailOptionNo.ProductOptionId
ON dbo.SerialModelGroupOptions.OptionPartNo = dbo.SerailOptionNo.PartNo
LEFT OUTER JOIN
dbo.GroupConfig ON dbo.SerialOptCon.GROUPID = dbo.GroupConfig.ID
LEFT OUTER JOIN
(SELECT itm1.PartNo, itm1.Revision, itm1.RevName
FROM dbo.Items AS itm1
INNER JOIN
(SELECT PartNo, MAX(Revision) AS Revision
FROM dbo.Items
GROUP BY PartNo) AS itm2 ON itm1.PartNo = itm2.PartNo
AND itm1.Revision = itm2.Revision) AS TopRevsion ON dbo.SerailOptionNo.PartNo = TopRevsion.PartNo
WHERE
(dbo.SerialOptCon.GROUPID = '01d2b4d2-ad63-4eab-a83f-f31ea0f6274b')
AND (dbo.SerialModelGroupOptions.MODEL = 'abcde')
ORDER BY
SEQUENCE
Here is the LINQ query that I have currently:
foreach (var item in priceGroupDetailsViewModels)
{
(from pmo in _context.SerialModelGroupOptionss
join po in _context.SerailOptionNos on pmo.OptionPartNo equals po.PartNo
join pop in _context.dbo.SerialOptCon on po.SerailOptionNoId equals pop.SerailOptionNoId
join con in _context.GroupConfig on pop.GroupId equals con.GroupId
into p
from pmp in p.DefaultIfEmpty()
join it in _context.Items on po.PartNo equals it.PartNo
where pmp.PriceGroupId == item.PriceGroupId && pmo.Model == item.Name
select new OptionsDetails
{
GroupId = pop.GroupId,
PartNo = po.PartNo,
Currency = pmp.Currency,
Sequence = pmo.Sequence,
Description = it.Description3
});
}

How can I GROUP a LINQ query that includes multiple joined tables with ambiguous column names?

Here is my SQL-Query that I want to convert to LINQ:
SELECT artikel.id, charge.id
from Lots charge
JOIN Product artikel on charge.artikel = artikel.id
JOIN StorageQuantity_TAB lmengeTAB on charge.id = lmengeTAB.charge
JOIN StorageQuantity lmenge on lmengeTAB.id = lmenge.id
GROUP BY artikel.id, charge.id
That´s what I got so far:
var query = (from charge in Lots
join artikel in PartProducts on charge.Artikel equals artikel.Id
join lmengeTAB in StorageQuantity_TABs on charge.Id equals lmengeTAB.Charge
join lmenge in StorageQuantities on lmengeTAB.Id equals lmenge.Id
select new
{
artikel.Id,
chargeID = charge.Id,
}).ToList();
So how do I include that GROUP BY statement?

How can i select the most recent purchase by the Clients?

In a database with several tables and relationships between them, I want to select only the most recent payment by the clients last year.
My query goes like this:
SELECT
P.Name,
EV.EventName,
FN.Installments,
FN.PurchaseValue,
FN.DueDate
FROM ClientPrivate PF
JOIN Client P ON P.PesControle = PF.PesControle
JOIN ClientClass CP ON P.PesControle = CP.PesControle
JOIN EVENT EV ON CP.EveControle = EV.EveControle
JOIN Class cc ON cc.CurControle = EV.CurControle
JOIN Finance FN ON FN.PesControle = P.PesControle
It returns the values I need, only I'd like to get only the most recent purchase by each client, instead of all of them.
I edited to help clarify. The 'Controle' columns are the keys.
Whatever you date column is put that in the ORDER BY Clause of the ROW_NUMBER() function and you are good to go.
;WITH CTE AS
(
SELECT
P.PesNome,
EV.EveDescri,
FN.FinTotParc,
FN.FinVlrLiquido,
FN.FinDiaVencto,
ROW_NUMBER() OVER (PARTITION BY P.PesNome ORDER BY [DateColumn] DESC) rn
FROM PessoaFisica PF
JOIN Pessoa P ON P.PesControle = PF.PesControle
JOIN CursoPessoa CP ON P.PesControle = CP.PesControle
JOIN EVENTO EV ON CP.EveControle = EV.EveControle
JOIN Curso cc ON cc.CurControle = EV.CurControle
JOIN Financeiro FN ON FN.PesControle = P.PesControle
)
SELECT *
FROM CTE
WHERE rn = 1
I'm guessing Financeiro.FinDiaVencto is your purchase date, and that Pessoa.PesControle is a unique identifier for a person. If not, you'll need to modify the query. I'm using Google to translate Portuguese, and it's still not clear to me.
SELECT
P.PesNome,
EV.EveDescri,
FN.FinTotParc,
FN.FinVlrLiquido,
FN.FinDiaVencto
FROM PessoaFisica PF
JOIN Pessoa P
ON P.PesControle = PF.PesControle
JOIN CursoPessoa CP
ON P.PesControle = CP.PesControle
JOIN EVENTO EV
ON CP.EveControle = EV.EveControle
JOIN Curso cc
ON cc.CurControle = EV.CurControle
JOIN Financeiro FN
on FN.PesControle = P.PesControle
WHERE EXISTS (
SELECT 1
FROM PessoaFisica PF_s
JOIN Pessoa P_s
ON P_s.PesControle = PF_s.PesControle
JOIN CursoPessoa CP_s
ON P_s.PesControle = CP_s.PesControle
JOIN EVENTO EV_s
ON CP_s.EveControle = EV_s.EveControle
JOIN Curso cc_s
ON cc_s.CurControle = EV_s.CurControle
JOIN Financeiro FN_s
on FN_s.PesControle = P_s.PesControle
WHERE P_s.PesControle = P.PesControle
HAVING MAX(FN_s.FinDiaVencto) = FN.FinDiaVencto
)
That should return the record with the most recent Financeiro.FinDiaVencto for each Pessoa.PesControle. If you need it by course or by event(?) you'll need to modify the WHERE clause to join on those fields, too.
I'm not sure if that join to PessoaFisica is necessary either, but I included it because it could be eliminating records. You can include a GROUP BY in the subquery, but it should be redundant.

Translate from SQL to Linq

I'm really new with this whole Linq story, but I have a good understanding in SQL.
I need to rebuild a query from SQL to Linq. My SQL query is working perfectly and so far I have tried to do something by myself with Linq but without a good result...
Is it possible that someone could help me to translate this query from SQL to Linq?
I'm really ready to learn something new in this whole story. It would be nice if you could why is it working like that in linq.
SQL Statement
SELECT TimeReport.EntryDate
, SUM(TimeReport.Hours) AS Hours
, SUM(BillingRate.HourlyRate * TimeReport.Hours) AS Amount
FROM BillingRate
INNER JOIN Activity
ON BillingRate.BillingRateId = Activity.BillingRateIt
INNER JOIN TimeReport
ON Activity.ActivityId = TimeReport.ActivityId
RIGHT OUTER JOIN Dossier
ON TimeReport.DossierId = Dossier.DossierId
INNER JOIN LBU
ON Dossier.LBUId = LBU.LBUId
INNER JOIN BU
ON Dossier.BUId = BU.BUId
GROUP BY TimeReport.EntryDate
HAVING SUM(TimeReport.Hours) > 0
ORDER BY TimeReport.EntryDate desc
What I have tired with Linq
var x = (from br in ctx.BillingRate
join a in ctx.Activity on br.BillingRateId equals a.BillingRateIt
join tr in ctx.TimeReport on a.ActivityId equals tr.ActivityId
select br)
.GroupJoin(
(from d in ctx.Dossier
join l in ctx.LBU on d.LBUId equals l.LBUId
join b in ctx.BU on d.DossierId equals
Thanks for help and fast answer.
I appreciated every effort !!
Joins aren't necessary when you have navigation properties. Since I don't know exactly what your model looks like, use the following as a starting point and adjust to your own specifications:
// starting with Dossier handles the right outer join condition
var timeRecordQuery =
from d in ctx.Dossier
from tr in d.TimeReports
// this handles the inner join conditions after the right outer join
where d.LBUId.HasValue && d.BUId.HasValue
// project what you want to use
select new
{
EntryDate = tr.EntryDate,
Hours = tr.Hours,
// simply use navigation properties, no need to join
Amount = tr.Activity.BillingRate.HourlyRate * tr.Hours
};
var resultsQuery = from e in timeRecordQuery
// group by EntryDate
group e by e.EntryDate into g
// get sum of hours for each EntryDate value
let hours = g.Sum( x => x.Hours )
// having clause
where hours > 0
// project results
select new
{
EntryDate = g.Key,
Hours = hours,
Amount = g.Sum( x => x.Amount )
};

Group By Having and Count as LINQ query with multiply nested tables

I have the following SQL query to return all Customers who have no OrderLines with no Parts assigned - i.e. I only want the customers within which every order line of every order has no parts assigned - (in the actual problem I am dealing with a different domain but have translated to customers/orders to illustrate the problem)
SELECT c.Customer_PK
FROM Customers c
INNER JOIN Orders o
ON c.Customer_PK = o.Customer_FK
LEFT OUTER JOIN OrderLines l
ON o.Order_PK = l.Order_FK
LEFT OUTER JOIN Parts p
ON l.OrderLine_PK = p.OrderLine_FK
GROUP BY c.Customer_PK
HAVING COUNT(p.Part_PK) = 0
The best I have come up with in LINQ is as follows:
Dim qry =
(From c In context.Customers
Select New With { c.Customer_PK,
.CountParts =
(From o In c.Orders
From l In o.OrderLines
Select l.Parts.Count).DefaultIfEmpty.Sum})
qry = (From grp In qry
Where grp.CountParts = 0
Select grp.Customer_PK)
This works but generates less than optimal SQL - it is doing a subquery for Count on each row of the customers query rather than using Group By and Having. I tried making the LINQ Group By syntax work but it kept putting the filter as a WHERE not a HAVING clause.
Any ideas?
Edit in response to Answers below:
I am accepting JamieSee's answer as it addresses the stated problem, even though it does not produce the GROUP BY HAVING query I originally had.
Thanks Peter and Nick for your input on this. I am a VB developer so I have had a crack translating your code to VB, this is the closest I got to but it does not quite produce the desired output:
Dim qry = From c In context.Customers
Group Join o In context.Orders On c.Customer_PK Equals o.Customer_FK
Into joinedOrders = Group
From jo In joinedOrders.DefaultIfEmpty
Group Join l In context.OrderLines On jo.Order_PK Equals l.Order_FK
Into joinedLines = Group
From jl In joinedLines.DefaultIfEmpty
Group c By Key = New With {c.Customer_PK, jl} Into grp = Group
Where Key.jl Is Nothing OrElse Not Key.jl.Parts.Any
Select c.Customer_PK
The problem I had is that I have to push "jl" into the Group By "Key" so I can reference it from the Where clause, otherwise the compiler cannot see that variable or any of the other variables appearing before the Group By clause.
With the filter as specified I get all customers where at least one order has lines with no parts rather than only customers with no parts in any order.
Given that you don't care about the counts, only the resulting customers, consider the folllowing restatement of the problem:
Identify all Customers who do not have any Orders that have Lines with Parts.
This yields:
var customersWithoutParts = from c in Context.Customers
where !(from o in Context.Orders
from l in o.Lines
from p in l.Parts
select o.Customer_FK).Contains(c.Customer_PK)
select c.Customer_PK;
This should yield emitted SQL that is roughly equivalent to the following:
SELECT c.Customer_PK
FROM Customers AS c
WHERE (NOT EXISTS
(SELECT o.Cusomer_FK
FROM Orders AS o INNER JOIN
OrderLines AS l ON o.Order_PK = l.Order_FK INNER JOIN
Parts AS p ON l.OrderLine_PK = p.OrderLine_FK
WHERE (o.Customer_FK = c.Customer_PK)))
To get the SQL you were trying to reproduce, I'd start by trying the following:
var customersWithoutParts = from c in Context.Customers
from o in c.Orders.DefaultIfEmpty()
from l in o.Lines.DefaultIfEmpty()
join part in Context.Parts on part.OrderLine_FK equals l.OrderLine_PK into joinedParts
where joinedParts.Count() == 0
select c.Customer_PK;
Note that in VB the join here would be replaced by Group Join.
Just a tipp bocuse hard to create a query without the generated modells (C#):
from o in dc.Orders
join jOrderLines in dc.OrderLines on o.Order_PK equals jOrderLines.Order_FK into joinedOrderlines
from l in joinedOrderLines.DefaultIfEmpty()
group o by o.Customer_FK into g
where l == null || l.Count(x => x.Parts) == 0
select g.Key
What about something like this:
var qry = from c in db.Customers
join o in db.Orders.Where(x => x.Customer_FK == c.Customer_PK)
join l in db.OrderLines.Where(x => x.Order_FK = o.Order_PK).DefaultIfEmpty()
join p in db.Parts.Where(x => x.OrderLine_FK = l.OrderLine_PK).DefaultIfEmpty()
group c by new
{
c.Customer_PK
} into g
where g.Count(p => p.Part_PK != null) == 0
select new
{
Customer_PK = g.Key.Customer_PK
};