Запрос Linq возвращает дубликаты после левого внешнего соединения

#c# #linq

#c# #linq

Вопрос:

У меня есть table1 и table2, обе имеют уникальные записи, и мне нужно левое внешнее соединение, чтобы получить ОПИСАНИЕ из table2.

таблица1:

 ID
SUBJECT
CATALOG_NBR
...
  

таблица2:

 SUBJECT
CATALOG_NBR
DESCR
...
  

Однако после левого внешнего соединения я получаю двойные записи. Я попробовал groupby и все равно возвращает дубликаты. Ниже приведен мой запрос:

 IQueryable<joinedTable> qry = (from a in db.table1
    join b in db.table2 on 
        new { SUBJECT=a.SUBJECT, CATALOG_NBR=a.CATALOG_NBR } equals
        new { SUBJECT = b.SUBJECT, CATALOG_NBR = b.CATALOG_NBR } 
    into ab from x  in ab.DefaultIfEmpty()
    select new joinedTable()
    {
        ID=a.ID,
        SUBJECT=a.SUBJECT,
        CATALOG_NBR=a.CATALOG_NBR,
        DESCR=x.DESCR
    }
    .GroupBy(a=>a.ID)
    .Select(a=>a.FirstOrDefault())
    .AsQueryable();
  

В чем проблема в моем запросе? Спасибо.

Комментарии:

1. Вместо new {SUBJECT=a.SUBJECT, CATALOG_NBR=a.CATALOG_NBR } используйте new {a.SUBJECT, a.CATALOG_NBR } . Linq не знает, как сравнивать с именованным объектом. Являются ли строки темы и каталога строками?

2. Спасибо. да. Это строки. Я пробовал ваш способ, но он по-прежнему возвращает дубликаты.

3. У вас дублирующиеся идентификаторы? У вас должен быть только один из каждого идентификатора. Похоже, вам не хватает круглой скобки перед GroupBy : }). GroupBy(a=>a.ID ).Выберите(a=>a.FirstOrDefault()).AsQueryable();

4. поле ID является уникальным целым числом в table1. В таблице 2 нет поля ID.

5. Таким образом, вы можете получить <id,subject,CATALOB_NBR> : <123,math, Math101> и <124,math, Math101>

Ответ №1:

Таким образом, строки Table1 и Table2 имеют Subject и CatalogNr . И вам нужны «Строки таблицы1, каждая из которых содержит ноль или более строк таблицы2, которые имеют одинаковое значение Subject и CatalogNr»

Всякий раз, когда вам нужны элементы с нулевым или более подпунктами, например, клиенты с их заказами, школы с их учениками, Table1Rows с их Table2Rows, рассмотрите возможность использования одной из перегрузок Queryable .Групповое соединение.

 var result = dbContext.Table1Rows.GroupJoin(dbContext.Table2Rows,

    table1Row => new
    {
        Subject=table1Row.SUBJECT,
        CatalogNr=table1Row.CATALOG_NBR
    },
    table2Row => new
    {
        Subject=table2Row.SUBJECT,
        CatalogNr=table2Row.CATALOG_NBR
    },

    // parameter ResultSelector: take each row of Table1 and the zero or more matching
    // matching rows of Table2 to make one new:
    (table1Row, table2Rows) =>  new
    {
        Id = table1Row.Id,
        Subject = table1Row.SUBJECT,
        CatalogNr = table1Row.CATALOG_NBR,
        
        Table2Descriptions = table2Rows.Select(table2Row => table2Row.DESCR).ToList(),
    });
  

В словах:

  • Из каждой строки в Table1 создайте один новый объект, содержащий Subject и CatalogNr строки.
  • Из каждой строки в Table2 создайте один новый объект, содержащий Subject и CatalogNr строки.
  • Для каждой строки в Table1 и всех нулевых или более совпадающих строк Table2 создайте один новый объект, содержащий несколько свойств строки из table1 и описания свойств, которые представляют собой список всех значений ОПИСАНИЯ соответствующих строк table2.

«Клиенты со своими заказами» выглядят более естественно, чем повторяющиеся комбинации [Клиент 1, заказ 1] [Клиент 1, заказ 2] [Клиент 2, заказ 3] и т.д. Другим преимуществом группового объединения является то, что данные каждого Клиента передаются только один раз.

Одно существенное отличие: с внешним соединением слева вы не получите клиентов, у которых нет заказов. С GroupJoin у вас будет каждый клиент, даже если у них нет заказов. Если вы этого не хотите, добавьте простое where:

 // keep only the table1 items that have at least one matching table2 item
.Where(groupJoinResult => groupJoinResult.Descriptions.Any())
  

И если вам действительно нужно сглаженное внешнее соединение слева, используйте SelectMany . Лично я думаю, что для этого нет веской причины.