【问题标题】:Outer join two tables with null values外连接两个空值的表
【发布时间】:2015-06-19 15:38:20
【问题描述】:

我有这个数据结构:

class Person
{
    public string FirstName { get; set; }
}

class Pet
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Link
{
    public Person Person { get; set; }
    public int PetId { get; set; }
}

还有这个数据:

List<Person> people = new List<Person>
{
    new Person {FirstName = "Foo"},
    new Person {FirstName = "Bar"}
};
List<Pet> pets = new List<Pet>
{
    new Pet {Id = 1, Name = "FooBoy"},
};
List<Link> links = new List<Link>
{
    new Link {Person = people.First(), PetId = pets.First().Id}
};

现在我想获取所有人及其宠物名称的列表(如果没有宠物,则为 null) 结果应该是

Foo - FooBoy
Bar - null

我试过了

var query = from person in people
    join lnk in links on person equals lnk.Person into linkPets
    from link in linkPets.DefaultIfEmpty()
    join p in pets on link.PetId equals p.Id into subPets
    from subPet in subPets.DefaultIfEmpty()
    select new { person.FirstName, PetName = (subPet == null ? String.Empty : subPet.Name) };

但我在link.Pet 上得到空引用异常。如果我删除linkPets.DefaultIfEmpty(),我只会得到第一个人。

【问题讨论】:

  • 如果有没有主人的宠物,你也需要那个吗?
  • 不,我不需要没有主人的宠物

标签: c# linq outer-join


【解决方案1】:

另一种解决方案是使用 linkPets.DefaultIfEmpty(new Link()) (可能仅适用于加入内存,因为您原来的加入适用于数据库)

 var query = from person in people
                   join lnk in links on person equals lnk.Person into linkPets
                   from link in linkPets.DefaultIfEmpty(new Link())
                   join p in pets on link.PetId equals p.Id into subPets
                   from subPet in subPets.DefaultIfEmpty()
                   select new { person.FirstName, PetName = (subPet == null ? String.Empty : subPet.Name) };

【讨论】:

    【解决方案2】:

    问题改变之前

    (最初链接表有对宠物的引用,而不是 ID。)

    在我看来您只需要一个连接,因为您可以使用链接中的 Pet 属性,其中有一个:

    var query = from person in people
                join lnk in links on person equals lnk.Person into linkPets
                from link in linkPets.DefaultIfEmpty()
                select new { person.FirstName,
                             PetName = link == null ? "" : link.Pet.Name };
    

    问题改变后

    我保留了上面的原始解决方案,因为很容易将修改后的问题转换为原始问题 - 只需先在 petslinks 之间进行连接:

    var fullLinks = from link in links
                    join pet in pets on link.PetId equals pet.Id
                    select new { link.Person, Pet = pet };
    
    var query = from person in people
        join lnk in fullLinks on person equals lnk.Person into linkPets
        from link in linkPets.DefaultIfEmpty()
        select new { person.FirstName,
                     PetName = link == null ? "" : link.Pet.Name };
    

    如果你愿意,你可以在一个语句中做到这一点,但我不会:

    var query = from person in people
        join lnk in (from link in links
                     join pet in pets on link.PetId equals pet.Id
                     select new { link.Person, Pet = pet } )
          on person equals lnk.Person into linkPets
        from link in linkPets.DefaultIfEmpty()
        select new { person.FirstName,
                     PetName = link == null ? "" : link.Pet.Name };
    

    【讨论】:

    • 我认为我的任务过于简单,您找到了一个简单的解决方案。我更新了问题。最主要的是:我实际上在 Link 中没有 Pet 对象。我只有一个 ID。
    【解决方案3】:

    如果您想通过链接表同时获取人和宠物,您可以执行以下操作:

    var result= (
            from person in people
            from lnk in links
                .Where (w =>w.Person==person).DefaultIfEmpty()
            from p in pets
                .Where (w =>w==(lnk==null?null:lnk.Pet)).DefaultIfEmpty()
            select new { person.FirstName, PetName = (p == null ? String.Empty : p.Name) }
        ).Dump();
    

    【讨论】:

      猜你喜欢
      • 2012-08-14
      • 2015-07-13
      • 2016-08-30
      • 2021-05-08
      • 2021-08-01
      • 2015-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多