#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 . Лично я думаю, что для этого нет веской причины.