2011-10-01 115 views
3

我需要一些Linq自聯接的幫助。Linq自聯接查詢

我有以下班組長:

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 
............................................................ 

我需要的表1結果解析爲車主以最快的方式可能清單。 注意:類型可以爲空,但我仍然需要顯示所有者(所以,我假設左連接應該工作)。

這是我做的。 (業主是包含表1結果的WebService類)

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不知道如何在可選的where子句中使用您在示例中提供的內容。

+1

是*是*您實際的表?讓你進入正常化室。 –

+0

謝謝。不是我的。我從現有的web服務中獲得了它。 :) – user194076

+0

相關:[用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

我認爲輸入文件上的一個連接應該這樣做(尚未測試它),其中'元素1 = TypeId'和'Item2 = TypeCodeId'在'Tuple ' – Magnus

3

由於表格沒有正常化,我們需要從對象/表格中獲取distict用戶。這可以實現:

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