Group By拥有和计数为具有乘法嵌套表的LINQ查询

时间:2012-02-28 17:11:57

标签: sql vb.net linq linq-to-entities entity-framework-4.1

我有以下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和Having。我尝试使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”推入Group By“Key”,以便我可以从Where子句中引用它,否则编译器无法看到该变量或该组之前出现的任何其他变量按条款。

根据指定的过滤器,我得到所有客户,其中至少有一个订单的行没有任何部分,而不是只有没有任何部件的客户。

3 个答案:

答案 0 :(得分:2)

鉴于您不关心计数,只考虑最终客户,请考虑以下重述问题:

识别所有没有任何订单行的客户。

这会产生:

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

答案 1 :(得分:0)

在没有生成的modlls(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

答案 2 :(得分:0)

这样的事情:

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
            };