具有多重嵌套表的分组方式和计数作为 LINQ 查询
Posted
技术标签:
【中文标题】具有多重嵌套表的分组方式和计数作为 LINQ 查询【英文标题】:Group By Having and Count as LINQ query with multiply nested tables 【发布时间】:2012-02-28 17:11:56 【问题描述】:我有以下 SQL 查询来返回所有没有 OrderLines 且没有分配部件的客户 - 即我只想要每个订单的 每个 订单行没有分配部件的客户 - (在我正在处理不同域但已翻译给客户/订单以说明问题的实际问题)
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
我在 LINQ 中想出的最好的方法如下:
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)
这可行,但生成的 SQL 不是最优的 - 它正在对客户查询的每一行执行 Count 子查询,而不是使用 Group By 和 Have。我尝试使 LINQ Group By 语法工作,但它一直将过滤器作为 WHERE 而不是 HAVING 子句。
有什么想法吗?
根据以下答案进行编辑:
我接受 JamieSee 的回答,因为它解决了所述问题,即使它没有产生我最初的 GROUP BY HAVING 查询。
感谢 Peter 和 Nick 对此提供意见。我是一名 VB 开发人员,所以我在将您的代码翻译成 VB 时遇到了困难,这是我最接近的,但它并不能完全产生所需的输出:
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
我遇到的问题是我必须按“键”将“jl”推入组中,以便我可以从 Where 子句中引用它,否则编译器无法看到该变量或出现在组之前的任何其他变量按子句。
使用指定的过滤器,我得到所有客户,其中至少一个订单的行没有零件,而不是只有客户没有任何订单的零件。
【问题讨论】:
【参考方案1】:鉴于您不关心计数,只关心最终客户,请考虑以下重述问题:
识别所有没有包含零件行的订单的所有客户。
这会产生:
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;
这应该会产生大致相当于以下内容的发出的 SQL:
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)))
要获得您试图重现的 SQL,我将首先尝试以下操作:
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;
请注意,在 VB 中,此处的 join
将替换为 Group Join
。
【讨论】:
谢谢你,我将接受这个答案,因为它适用于我给出的示例并给出合理的 SQL。如果我要求的不是 NO 部分,那么我相信这个答案不再有效,因为 NOT EXISTS 必然意味着 COUNT = 0。更广泛的问题仍然是如何说服 LINQ-to-Entities 生成 SQL GROUP BY 和有类似例子的语法。 用我认为应该产生原始 SQL 的东西更新了答案。我没有测试更新的部分。让我知道这对你有什么作用。【参考方案2】:在没有生成的模型 (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
【讨论】:
【参考方案3】:这样的事情怎么样:
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
;
【讨论】:
以上是关于具有多重嵌套表的分组方式和计数作为 LINQ 查询的主要内容,如果未能解决你的问题,请参考以下文章