Запрос левого соединения не работает должным образом - PullRequest
0 голосов
/ 19 октября 2011

Как написать этот запрос в Linq to Entities:

SELECT * FROM TableA a LEFT JOIN TableB b ON a.Id = b.TableAId WHERE ISNULL(b.Id) OR b.FieldA = 1

Отношение 1-ко-многим, но ограничение b.FIeldA = 1 гарантирует, что фактические возвращаемые данные будут 1-0 ... 1, что означает, что количество возвращаемых записей должно быть равно записям в Таблице A , Мне нужно получить все данные из Таблицы A, и объединенные данные из Таблицы B, где существует. Я пытаюсь с этим запросом, который на самом деле преформ, как INNER JOIN (записи из TableA, которые не имеют связанных записей в TableB, не извлекаются)

Владислав предложил список запросов ниже, к которому я добавил дополнительную фильтрацию в первом запросе:

var query = (from x in myParentClasses.Include(x => x.TableBChildren)
         where !x.TableBChildren.Any(y => y.FieldA == 1)
         select x)
        .Concat(
         from x in myParentClasses.Include(x => x.TableBChildren)
         where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null)
         select x)
        .ToList();

Сгенерированный sql выглядит так:

SELECT   [Project1][...]
FROM     (SELECT [Extent1].[...],
                 [Extent2].[...],
                 [Extent3].[...],
                 CASE 
                   WHEN ([Extent3].[Id] IS NULL) THEN CAST(NULL AS int)
                   ELSE 1
                 END AS [C1]
          FROM   [dbo].[ParentTable] AS [Extent1]
                 LEFT OUTER JOIN [dbo].[ReferenceTable] AS [Extent2]
                   ON [Extent1].[ReferenceId] = [Extent2].[Id]
                 LEFT OUTER JOIN [dbo].[ChildrenTable] AS [Extent3]
                   ON [Extent1].[Id] = [Extent3].[ParentId]
          WHERE  [Extent1].[FieldA] = 1 /* @p__linq__0 */) AS [Project1]
ORDER BY [Project1].[Id] ASC,
         [Project1].[Id1] ASC,
         [Project1].[C1] ASC

Спасибо, Goran

1 Ответ

0 голосов
/ 19 октября 2011

Linq-to-entity не работает точно так же, как SQL.Он выполняет левое соединение, только если ваш запрос позволяет это (у вас нет ручного управления этим поведением, если вы не пишете соединение вручную).Это не случай вашего запроса, потому что он использует x.TableBChildren.Any = он требует, чтобы в родительской таблице была любая запись из дочерней таблицы, удовлетворяющая условию, поэтому он использует внутреннее соединение.

Я не пробовал и естьвозможно, лучший способ, но я думаю, что это может работать:

var query = (from x in myParentClasses.Include(x => x.TableBChildren)
             where !x.TableBChildren.Any()
             select x)
            .Concat(
             from x in myParentClasses.Include(x => x.TableBChildren)
             where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null)
             select x)
            .ToList();

Первая часть выбирает все записи, которые не имеют связанных сущностей, а вторая часть выбирает записи со связанными сущностями, удовлетворяющими вашему условию.Он объединяет эти две части.

Также помните, что Include не будет фильтровать ваши отношения - он будет выбирать все из них каждый раз.Если вы хотите отфильтровать их, вы должны написать запрос с проекцией.

Редактировать:

Если вы хотите отфильтровать TableB, вы не можете использовать обычный запрос и Include.Вы также не можете вернуть экземпляры MyParentClass.Вы должны использовать проекцию:

var query = from x in myParentClasses
            select new 
              {
                  Id = x.Id,
                  // Rest of properties from myParentClass you want to receive
                  TableBChildren = x.TableBChildren.Where(y => y.FieldA == 1 || y.Id == null)
              };

Это вернет анонимный тип с отфильтрованными TableB с.Невозможно проецировать в сопоставленную сущность (MyParentClass).

...