Сгруппировать, имея и считать как запрос LINQ с несколькими вложенными таблицами - PullRequest
0 голосов
/ 28 февраля 2012

У меня есть следующий запрос SQL, чтобы вернуть всех клиентов, у которых нет строк заказов без назначенных деталей - т.е. я хочу только клиентов, в которых каждая строка заказа каждого заказа не имеет назначенных частей - (в актуальная проблема Я имею дело с другим доменом, но перевёл клиентов / заказов для иллюстрации проблемы)

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, который у меня изначально был.

Спасибо, Питер и Ник за ваш вклад в это. Я разработчик 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, иначе компилятор не сможет увидеть эту переменную или любую другую переменную, появляющуюся перед группой. По пункту.

С указанным фильтром я получаю всех клиентов, у которых хотя бы в одном заказе есть строки без деталей, а не только у клиентов без деталей в любом заказе.

Ответы [ 3 ]

2 голосов
/ 29 февраля 2012

Учитывая, что вас не волнует подсчет, только получающиеся в результате клиенты учитывают следующую формулировку проблемы:

Определите всех клиентов, у которых нет заказов с линиями с деталями.

Это дает:

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.

0 голосов
/ 28 февраля 2012

Как насчет этого:

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
            };
0 голосов
/ 28 февраля 2012

Просто сложно сосредоточиться на создании запроса без сгенерированных моделей (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
...