您将如何使用LINQ进行“不参加”查询?


307

我有两个集合,两个集合中都具有属性Email。我需要获取第一个列表中Email第二个列表中不存在的项目的列表。对于SQL,我只会使用“ not in”,但我不知道LINQ中的等效项。怎么做?

到目前为止,我已经加入了,例如...

var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };

但是我不能加入,因为我需要区别,加入会失败。我需要某种使用包含或存在的方法,我相信。我只是还没有找到执行此操作的示例。


3
请注意,Echostorm的回答使代码更清晰比罗伯特的读
内森·库普

Answers:


302

我不知道这是否对您有帮助。

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers    
    where !(from o in dc.Orders    
            select o.CustomerID)    
           .Contains(c.CustomerID)    
    select c;

foreach (var c in query) Console.WriteLine( c );

在LINQ的NOT IN子句,SQL马可·鲁索


但是我对实体使用linq,所以我得到“只能使用原始类型的错误”。周围有什么工作吗?除了手动迭代和查找列表之外。
新手

13
对于LINQ to Entities,这对我来说效果很好。SQL成为WHERE NOT EXISTS(子查询)查询。也许有解决此问题的更新?
scottheckel 2012年

2
我认为EF的新版本做支撑。载,再加上这个问题不标记EF(版本)或LinqToSQL ..所以可能有必要范围的问题,在这里回答..
布雷特·卡斯威尔

4
@Robert Rouse-链接到linq中的The Not in cluse中的sql不再有效。只是个小气。
JonH

提供的链接指向标记为包含恶意软件的网站。
mikesigs

334

您需要Except运算符。

var answer = list1.Except(list2);

更好的解释在这里:https : //docs.microsoft.com/archive/blogs/charlie/linq-farm-more-on-set-operators

注意:由于必须实现IEqualityComparer才能将Except方法用于复杂类型,因此该技术仅对原始类型最有效。


7
使用Except:如果您使用复杂类型列表,则必须实现IEqualityComparer <MyComlplexType>,这使其不太好
sakito 2010年

4
你不具备实现的IEqualityComparer <T>如果你只是想比较引用相等,或者如果你覆盖T.Equals()和T.GetHashCode()。如果未实现IEqualityComparer <T>,则将使用EqualityComparer <T>。将使用默认值。
piedar

2
@Echostorm(和其他阅读),如果您执行“选择匿名”对象,则HashCode将由属性值确定;list1.Select(item => new { Property1 = item.Property1, Property2 = item.Property2 }).Except(list2.Select( item => new { Property1 = item.Property1, Property2 = item.Property2 }));当您通过仅评估一组复杂类型的值来确定相等性时,这特别有用。
Brett Caswell 2015年

3
实际上,下面有人指出,我正确地认为,IEquatityComparor<T,T>LinqToSql场景中不需要实现或重写对象比较方法。对于,查询将表示为/编译为/表示为SQL;因此将检查值,而不是对象引用。
Brett Caswell 2015年

2
使用exceptI,我可以将LINQ查询从8-10秒加速到半秒
Michael Kniskern

61

对于从一组内存对象开始并针对数据库进行查询的人,我发现这是最好的方法:

var itemIds = inMemoryList.Select(x => x.Id).ToArray();
var otherObjects = context.ItemList.Where(x => !itemIds.Contains(x.Id));

WHERE ... IN (...)在SQL中产生了一个不错的子句。


1
实际上,您可以在3.5中做到这一点
George Silva

59

第一个列表中的项目,而第二个列表中不存在电子邮件。

from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;

16

您可以使用Where和Any的组合来查找不在:

var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email  == p.Email));

8

您可以将两个集合放在两个不同的列表中,例如list1和list2。

然后写

list1.RemoveAll(Item => list2.Contains(Item));

这将起作用。


3
很好,但是具有从列表中删除元素的副作用。
塔里克2014年

7

在使用ADO.NET实体框架的情况下,EchoStorm的解决方案也可以完美地工作。但是我花了几分钟把头缠起来。假设您具有数据库上下文dc,并且想要在表x中查找未在表y中链接的行,则完整的答案如下所示:

var linked =
  from x in dc.X
  from y in dc.Y
  where x.MyProperty == y.MyProperty
  select x;
var notLinked =
  dc.X.Except(linked);

对于Andy的评论,是的,在LINQ查询中可以有两个from。这是一个使用列表的完整工作示例。Foo和Bar每个类都有一个ID。Foo通过Foo.BarId具有对Bar的“外键”引用。该程序将选择所有未链接到相应Bar的Foo。

class Program
{
    static void Main(string[] args)
    {
        // Creates some foos
        List<Foo> fooList = new List<Foo>();
        fooList.Add(new Foo { Id = 1, BarId = 11 });
        fooList.Add(new Foo { Id = 2, BarId = 12 });
        fooList.Add(new Foo { Id = 3, BarId = 13 });
        fooList.Add(new Foo { Id = 4, BarId = 14 });
        fooList.Add(new Foo { Id = 5, BarId = -1 });
        fooList.Add(new Foo { Id = 6, BarId = -1 });
        fooList.Add(new Foo { Id = 7, BarId = -1 });

        // Create some bars
        List<Bar> barList = new List<Bar>();
        barList.Add(new Bar { Id = 11 });
        barList.Add(new Bar { Id = 12 });
        barList.Add(new Bar { Id = 13 });
        barList.Add(new Bar { Id = 14 });
        barList.Add(new Bar { Id = 15 });
        barList.Add(new Bar { Id = 16 });
        barList.Add(new Bar { Id = 17 });

        var linked = from foo in fooList
                     from bar in barList
                     where foo.BarId == bar.Id
                     select foo;
        var notLinked = fooList.Except(linked);
        foreach (Foo item in notLinked)
        {
            Console.WriteLine(
                String.Format(
                "Foo.Id: {0} | Bar.Id: {1}",
                item.Id, item.BarId));
        }
        Console.WriteLine("Any key to continue...");
        Console.ReadKey();
    }
}

class Foo
{
    public int Id { get; set; }
    public int BarId { get; set; }
}

class Bar
{
    public int Id { get; set; }
}

LINQ有两个问题吗?那会有所帮助。
安迪2010年

安迪:是的,请参阅上面的修订答案。
Brett 2010年

4
var secondEmails = (from item in list2
                    select new { Email = item.Email }
                   ).ToList();

var matches = from item in list1
              where !secondEmails.Contains(item.Email)
              select new {Email = item.Email};

4

一个人也可以使用 All()

var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));

2

虽然Except是答案的一部分,但不是全部答案。默认情况下,Except(与LINQ运算符一样)对引用类型进行引用比较。要通过对象中的值进行比较,您必须

  • IEquatable<T>以您的类型实施,或
  • 覆盖EqualsGetHashCode输入您的类型,或者
  • 传入IEqualityComparer<T>为您的类型实现的类型的实例

2
...如果我们在谈论LINQ to Objects。如果是LINQ to SQL,则查询将转换为在数据库上运行的SQL语句,因此这并不适用。
卢卡斯

1

为了简单起见,使用int列表的示例。

List<int> list1 = new List<int>();
// fill data
List<int> list2 = new List<int>();
// fill data

var results = from i in list1
              where !list2.Contains(i)
              select i;

foreach (var result in results)
    Console.WriteLine(result.ToString());

1

对于还想IN在C#中使用类似SQL的运算符的任何人,请下载以下软件包:

尼斯·林克

它具有InNotIn方法:

var result = list1.In(x => x.Email, list2.Select(z => z.Email));

即使你可以用这种方式

var result = list1.In(x => x.Email, "a@b.com", "b@c.com", "c@d.com");

0

谢谢布雷特。你的建议对我也有帮助。我有一个对象列表,并希望使用另一个对象列表进行过滤。再次感谢....

如果有人需要,请查看我的代码示例:

'First, get all the items present in the local branch database
Dim _AllItems As List(Of LocalItem) = getAllItemsAtBranch(BranchId, RecordState.All)

'Then get the Item Mappings Present for the branch
Dim _adpt As New gItem_BranchesTableAdapter
Dim dt As New ds_CA_HO.gItem_BranchesDataTable
    _adpt.FillBranchMappings(dt, BranchId)

Dim _MappedItems As List(Of LocalItem) = (From _item As LocalItem In _AllItems Join _
    dr As ds_CA_HO.gItem_BranchesRow In dt _
    On _item.Id Equals dr.numItemID _
    Select _item).ToList

_AllItems = _AllItems.Except(_MappedItems.AsEnumerable).ToList

 Return _AllItems

0

我没有使用LINQ to Entities进行测试:

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers 
    where !dc.Orders.Any(o => o.CustomerID == c.CustomerID)   
    select c;

或者:

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers 
    where dc.Orders.All(o => o.CustomerID != c.CustomerID)   
    select c;

foreach (var c in query) 
    Console.WriteLine( c );

0

您不能进行外部联接,仅在组为空时才从第一个列表中选择项目吗?就像是:

Dim result = (From a In list1
              Group Join b In list2 
                  On a.Value Equals b.Value 
                  Into grp = Group
              Where Not grp.Any
              Select a)

我不确定使用Entity Framework是否可以以任何有效的方式工作。


0

或者,您可以这样做:

var result = list1.Where(p => list2.All(x => x.Id != p.Id));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.