LINQ를 사용하여 "not in"쿼리를 어떻게 수행 하시겠습니까?


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의 답변은 Robert의 것보다 훨씬 더 명확한 코드를 생성합니다.
Nathan Koop

답변:


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 );

에서 SQL에 LINQ에서 NOT IN 절 에 의해 마르코 루소


그러나 엔티티에 linq를 사용하므로 "기본 유형 만 오류를 사용할 수 있습니다"가 표시됩니다. 해결 방법이 있습니까 ...? 목록을 수동으로 반복하고 찾는 것 말고는
초보자

13
이것은 LINQ to Entities에서 잘 작동합니다. SQL은 WHERE NOT EXISTS (하위 조회) 조회가됩니다. 이 문제를 해결 한 업데이트가 있었습니까?
scottheckel

2
나는 새로운 버전의 EF가 .Contains를 지원한다고 생각한다. 또한이 질문은 EF (버전) 또는 LinqToSQL을 태그하지 않는다. 그래서 질문의 범위를 정하고 여기에 대답 할 필요가있을 수있다.
Brett Caswell

4
@Robert Rouse-linq에서 SQL의 cl in cluse에 대한 링크가 더 이상 작동하지 않습니다. 그냥 fyi.
JonH

제공된 링크는 멀웨어가 포함 된 것으로 플래그가 지정된 사이트로 연결됩니다.
mikesigs

334

제외 연산자를 원합니다.

var answer = list1.Except(list2);

여기에 더 나은 설명 : https://docs.microsoft.com/archive/blogs/charlie/linq-farm-more-on-set-operators

참고 : 이 기술은 복잡한 형식 의 메서드 를 사용하려면 IEqualityComparer 를 구현해야하므로 기본 형식에만 가장 적합 Except합니다.


7
제외 사용 : 복잡한 유형의 목록과 함께 작업하는 경우, 당신은 그것이되지 만드는 IEqualityComparer <MyComlplexType>를 구현해야 그 좋은
sakito

4
당신은하지 않습니다 그냥 참조 평등을 비교하려면 IEqualityComparer <T>를 구현하거나 재정의 T.Equals를 한 경우 ()와 T.GetHashCode (). IEqualityComparer <T>를 구현하지 않으면 EqualityComparer <T> .Default 가 사용됩니다.
piedar

2
@Echostorm (및 기타 읽기), 익명으로 선택 (Select to Anonymous) 개체를 수행하는 경우 HashCode는 속성 값에 의해 결정됩니다. list1.Select(item => new { Property1 = item.Property1, Property2 = item.Property2 }).Except(list2.Select( item => new { Property1 = item.Property1, Property2 = item.Property2 }));이것은 복잡한 유형의 값 집합 만 평가하여 평등을 결정할 때 특히 유용합니다.
Brett Caswell

3
실제로, 누군가 아래에 지적했으며 시나리오 IEquatityComparor<T,T>에서 객체 비교 방법 을 구현 하거나 재정 의 할 필요가 없다고 올바르게 생각합니다 LinqToSql. 쿼리는 SQL로 표현 / 컴파일 / SQL로 표현됩니다. 따라서 객체 참조가 아닌 값이 확인됩니다.
Brett Caswell

2
를 사용하면 exceptLINQ 쿼리 속도를 8-10 초에서 0.5 초로 단축 할 수있었습니다
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
멋지지만 목록에서 요소를 제거하면 부작용이 있습니다.
Tarik

7

ADO.NET Entity Framework를 사용하는 경우 EchoStorm의 솔루션도 완벽하게 작동합니다. 그러나 머리를 감싸는 데 몇 분이 걸렸습니다. 데이터베이스 컨텍스트 dc가 있고 테이블 y에서 링크되지 않은 테이블 x에서 행을 찾으려고 가정하면 전체 답변은 다음과 같습니다.

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 and Bar에는 ID가 있습니다. Foo에는 Foo.BarId를 통해 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에서 두 가지 장점이 있습니까? 도움이 될 것입니다.
Andy

Andy : 예, 위의 수정 된 답변을 참조하십시오.
Brett

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 문으로 변환되므로 적용되지 않습니다.
Lucas

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

INC #에서 SQL 유사 연산자 를 사용하려는 사람은 이 패키지를 다운로드하십시오.

Mshwf.NiceLinq

그것 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 프레임 워크에서 효율적인 방식으로 작동하는지 확실하지 않습니다.


0

또는 다음과 같이 할 수 있습니다 :

var result = list1.Where(p => list2.All(x => x.Id != p.Id));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.