【问题标题】:Linq self-join queryLinq 自联接查询
【发布时间】:2011-10-02 21:01:02
【问题描述】:

我需要一些有关 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
............................................................

我需要以最快的方式将 table1 的结果解析为所有者列表。 注意:Type 可以为 null,但我仍然需要显示该所有者(因此,我假设 left join 应该可以工作)。

这就是我的工作。 (owners是一个包含table1结果的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. 它返回多个结果,但 distinct 无法按预期工作
  2. 我尝试使用 GroupBy 但它说不能将 Owner 隐式转换为 IEnumerable &lt;int, Owner&gt;
  3. 超级慢

如何通过自联接获得不同的记录并提高性能? 谢谢

更新: 谢谢你们的回答,现在正在测试,但我发现我忘了提供另外一件事。我在表格布局中添加了一个名为 TypeCodeId 的新列(见上文) 用户可以根据他们的选择过滤值。所以,我有用于地区、城市和性别的 TypeCodeId + TypeValue 字典。所有这些参数都是可选的(如果用户没有选择任何参数,我只显示所有记录。

所以,假设用户选择了过滤器Area: Unites States和过滤器City: Los Angeles

他们我的查询应该是这样的:

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

如果只选择了 Area:Mexico,那么我的查询将如下所示:

Select Projects where Area equals Mexico(321)

我不确定如何使用您在示例中提供的内容来执行可选的 where 子句。

【问题讨论】:

标签: c# asp.net linq asp.net-4.0


【解决方案1】:

由于表格没有标准化,我们需要从对象/表格中获取不同的用户。这可以通过以下方式完成:

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, 
        };

您可能需要更改上述示例中的投影。

【讨论】:

    【解决方案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;
        }
    }
    

    要获得更多性能,您可以编写一个仅比较 int OwnerId 的 IEqualityComparer 类并将其发送到 Distinct 函数

    【讨论】:

    • 您的解决方案运行良好且速度非常快。你能看看我的更新吗?
    • 我认为输入文件管理器的连接应该在Item1 = TypeIdItem2 = TypeCodeId 中的Tuple&lt;int, int&gt; 中执行(尚未测试)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    相关资源
    最近更新 更多