2011-10-01 4 views
3

Мне нужна помощь с Linq self-join.Linq self-join query

У меня есть следующий classs:

public class Owner 
{ 
public string OwnerId {get;set;} 
public string Name {get;set;} 
public string Area {get;set;} 
public string City {get;set;} 
public string Sex {get;set;} 
public List<Dog> dog {get;set;} 
} 

И таблица ....

ID OwnerId OwnerName TypeId TypeName TypeValue  TypeCodeId 
1  1  John  1  Area  United States  440 
2  1  John  2  City  Los-Angeles   221 
3  1  John  3  Sex  Female    122 
4  2  Mary  1  Area  Mexico    321 
4  2  Mary  2  City  Cancun    345 
............................................................ 

Мне нужно анализировать результаты table1 в списке владельцев самый быстрый способ это возможно. Примечание: Тип может быть нулевым, но мне все еще нужно показать этого владельца (Итак, я полагаю, что левое соединение должно работать).

Вот что я делаю. (Владельцы класс вебсервис, который содержит результаты table1)

public IEnumerable<Owner> GetOwners() { 
    return (from owner in owners 
      join area in owners into owner_area 
      from oa in owner_area.DefaultIfEmpty() 
      join City in owners into owner_city 
      from oc in owner_city.DefaultIfEmpty() 
      join sex in owners into owner_sex 
      from os in owner_sex.DefaultIfEmpty() 
      where oa.TypeId == 1 && oc.TypeId ==2 && os.TypeId ==3 
      select new Owner() {OwnerId = owner.OwnerId, 
           Name = owner.Name, 
           Area = oa.TypeValue, 
           City = oc.TypeValue, 
           Sex = os.TypeValue}).Distinct(); 
} 

Этот запрос имеет несколько вопросов:

  1. возвращает несколько результатов и отчетливый не работает, как ожидалось
  2. Я пытался использовать GroupBy, но он говорит, что не может неявно преобразовать владельца в IEnumerable <int, Owner>
  3. это супер медленный

Как я могу получить отдельную запись с самостоятельным подключением и повышением производительности? Благодаря

UPDATE: Спасибо ребята за ваши ansewers, тестирование, но я понял, что я забыл поставить еще одну вещь. Я добавил новый столбец TypeCodeId в макет таблицы (см. Выше). Пользователь может фильтровать значения на основе их выбора. Итак, у меня есть словари TypeCodeId + TypeValue для области, города и пола. Все эти параметры являются необязательными (Если пользователь не выбрал какой-нибудь, я просто показать им все записи.

Итак, предположим, что пользователь выбрал фильтр Area: Unites States и фильтр City: Los Angeles

им мой запрос будет выглядеть это:

Select Projects where Area equals United States(440) and City equals Los Angeles(221) 

Если только площадь: Мексика была выбрана, то мой запрос будет читать что-то вроде этого:

Select Projects where Area equals Mexico(321) 

Я m не уверен, как делать необязательные, где предложения с тем, что вы предоставили в примерах.

+1

Is * это * фактическая таблицы? Взять тебя в камеру нормализации. –

+0

Спасибо. Не мой. Я получил его из существующего веб-сервиса. :) – user194076

+0

Дистанционно связанный: [Заменить цикл for-switch с запросом Linq] (http://stackoverflow.com/questions/7607629) – dtb

ответ

2

Для лучшей производительности, если вы думаете, что это способ сделать это.

public IEnumerable<Owner> GetOwners(IEnumerable<Tuple<int, int>> filter) 
{ 
    var q = (from o in owners 
      join f in filter on 
       new {o.TypeId, o.TypeCodeId} equals 
       new {TypeId = f.Item1, TypeCodeId = f.Item2} 
      select o).ToList(); 

    var dic = q.ToDictionary (o => new {o.OwnerId, o.TypeId}, o => o.TypeValue); 
    foreach (var o in q.Select(o => new { o.OwnerId, o.OwnerName }).Distinct()) 
    { 
     var owner = new Owner() 
     { 
      OwnerId = o.OwnerId, 
      Name = o.OwnerName 
     }; 
     string lookup; 
     if(dic.TryGetValue(new {o.OwnerId, TypeId = 1}, out lookup)) 
      owner.Area = lookup; 
     if(dic.TryGetValue(new {o.OwnerId, TypeId = 2}, out lookup)) 
      owner.City = lookup; 
     if(dic.TryGetValue(new {o.OwnerId, TypeId = 3}, out lookup)) 
      owner.Sex = lookup; 

     yield return owner; 
    } 
} 

Чтобы получить даже немного больше производительности, вы можете написать IEqualityComparer класс, который только сравнивает INT OwnerId и отправить его в функцию Distinct

+0

Ваше решение работает отлично и очень быстро. Вы можете увидеть мое обновление? – user194076

+0

Я думаю, что соединение на входном файле должно сделать это (не проверили его), где 'Item1 = TypeId' и' Item2 = TypeCodeId' в 'Tuple ' – Magnus

3

Поскольку таблица не нормирована, нам необходимо получить пользователей-участников из объектов/таблицы. Это может быть достигнуто с:

owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct() 

И тогда мы должны присоединиться к «типам» с двумя значениями соответствия, один для OwnerId и другой для конкретного типа.

var ownerQuery = 
    from o in owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct() 

    join area in owners on new { o.OwnerId, TypeId = 1 } equals new { area.OwnerId, area.TypeId } into areas 
    from area in areas.DefaultIfEmpty() 

    join city in owners on new { o.OwnerId, TypeId = 2 } equals new { city.OwnerId, city.TypeId } into cities 
    from city in cities.DefaultIfEmpty() 

    join sex in owners on new { o.OwnerId, TypeId = 3 } equals new { sex.OwnerId, sex.TypeId } into sexes 
    from sex in sexes.DefaultIfEmpty() 

    select new 
     { 
      owner = o, 
      Area = (area != null) ? area.TypeValue : null, 
      City = (city != null) ? city.TypeValue : null, 
      Sex = (sex != null) ? sex.TypeValue : null, 
     }; 

Возможно, вам потребуется изменить проекцию в приведенном выше примере.

+0

Можете ли вы посмотреть мое обновление? – user194076