linq를 사용하여 ID 목록을 기반으로 여러 레코드 선택


122

UserProfile테이블 의 ID가 포함 된 목록이 있습니다. 어떻게 내가 모두를 선택할 수 있습니다 UserProfiles나는에있어 아이디의 목록을 기반으로 var사용 LINQ?

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);

나는 바로 여기에 갇혔다. for 루프 등을 사용하여이 작업을 수행 할 수 있습니다.하지만 LINQ.


4
검색과 찾기는 서로 다른 두 가지입니다. 하지만 인터넷을 통해 내 어깨 너머로 볼 수 있기 때문에 내가 검색하지 않은 것을 어떻게 알 수 있습니까? 말하지 마! 맞아? 내 요점.
Yustme

5
질문을하는 것은 검색을하는 것보다 더 많은 시간이 소요됩니다. 다음에 '그 / 그녀'가 검색을했다고 가정합니다.
Yustme

2
이것은 여전히 ​​상당한 관심을 받고 있으므로 ReSharper가 반복 코드를 LINQ 문으로 변환 할 수있는 위치를 제안하는 데 매우 효과적이라고 언급하고 싶습니다. LINQ를 처음 접하는 사람들에게는이 목적만으로는 필수 도구가 될 수 있습니다.
Yuck 2014-06-12

답변:


206

그것을 위해 사용할 수 있습니다 Contains(). 실제로 IN절 을 생성하려고 할 때 약간 거꾸로 느껴지 지만 이렇게해야합니다.

var userProfiles = _dataContext.UserProfile
                               .Where(t => idList.Contains(t.Id));

또한 각 UserProfile레코드에 int Id필드 가 있다고 가정 합니다. 그렇지 않은 경우 그에 따라 조정해야합니다.


안녕하세요, 예, 사용자 프로필 레코드에 ID가 포함되어 있습니다. 그래서 어떻게 든 나는 t => t.id == idList.Contains (id)?
Yustme

Contains()id내가 답변에서 작성한 것처럼 사용하면 각 값 에 대한 동등성 검사를 처리합니다 . ==한 세트 (배열)의 항목을 다른 세트 (데이터베이스 테이블)와 비교하려고 할 때 명시 적으로 쓸 필요가 없습니다 .
Yuck 2013 년

문제는 t가 UserProfile의 전체 객체를 보유하고 idList는 int 만 포함한다는 것입니다. 컴파일러가 무언가에 대해 불평했지만 나는 그것을 고칠 수 있었다. 감사.
Yustme

1
@Yuck-나를 위해 작동하지 않습니다, 기능 시간이 초과되었다고 말합니다! 지연로드를 비활성화했지만 여전히 실패합니다.
bhuvin

1
"대리자 형식이 아니기 때문에 람다 식을 'int'형식으로 변환 할 수 없습니다."라는 메시지가 표시됩니다. 어떻게 고칠까요?
Stian

90

.Where 및 .Contains를 사용한 솔루션은 O (N 제곱)의 복잡성을가집니다. Simple .Join은 훨씬 더 나은 성능을 가져야합니다 (해싱으로 인해 O (N)에 가까움). 따라서 올바른 코드는 다음과 같습니다.

_dataContext.UserProfile.Join(idList, up => up.ID, id => id, (up, id) => up);

그리고 이제 측정 결과입니다. 100,000 개의 UserProfile과 100,000 개의 ID를 생성했습니다. Join은 32ms, .Where with .Contains는 2 분 19 초가 걸렸습니다! 이 테스트에서 내 진술을 증명하기 위해 순수 IEnumerable을 사용했습니다. IEnumerable 대신 List를 사용하면 .Where 및 .Contains가 더 빠릅니다. 어쨌든 그 차이는 중요합니다. 가장 빠른 .Where .Contains는 Set <>입니다. 모든 것은 .Contains에 대한 기본 콜레 션의 복잡성에 달려 있습니다. 봐 이 게시물 아래에있는 내 테스트 샘플에 complexity.Look LINQ에 대해 배울 수 :

    private static void Main(string[] args)
    {
        var userProfiles = GenerateUserProfiles();
        var idList = GenerateIds();
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        userProfiles.Join(idList, up => up.ID, id => id, (up, id) => up).ToArray();
        Console.WriteLine("Elapsed .Join time: {0}", stopWatch.Elapsed);
        stopWatch.Restart();
        userProfiles.Where(up => idList.Contains(up.ID)).ToArray();
        Console.WriteLine("Elapsed .Where .Contains time: {0}", stopWatch.Elapsed);
        Console.ReadLine();
    }

    private static IEnumerable<int> GenerateIds()
    {
       // var result = new List<int>();
        for (int i = 100000; i > 0; i--)
        {
            yield return i;
        }
    }

    private static IEnumerable<UserProfile> GenerateUserProfiles()
    {
        for (int i = 0; i < 100000; i++)
        {
            yield return new UserProfile {ID = i};
        }
    }

콘솔 출력 :

경과. 가입 시간 : 00 : 00 : 00.0322546

경과 .Where. 시간 포함 : 00 : 02 : 19.4072107


4
숫자로 뒷받침 할 수 있습니까?
Yustme 2014

좋지만 언제 List사용 되는지 궁금합니다 . +1
Yustme 2014

좋아, 관심있는 타이밍은 다음과 같습니다. List는 13.1 초가 걸렸고 HashSet은 0,7ms가 걸렸습니다! 따라서 .Where .Contains는 HashSet의 경우에만 가장 좋습니다 (.Contains에 복잡성 O (1)이있는 경우). 다른 경우에는 .Join이 더 좋습니다
David Gregor

5
내가 얻을 Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.LINQ2SQL의 데이터 컨텍스트를 사용할 때 오류가 발생했습니다.
Mayank Raichura

3
@Yustme-성능은 항상 고려 사항입니다. (나는 "이것은 받아 들여진 대답이어야한다"녀석이되기 싫지만 ...)
jleach

19

좋은 답변이 있지만 중요한 한 가지를 잊지 마십시오 . 다른 결과를 제공합니다!

  var idList = new int[1, 2, 2, 2, 2]; // same user is selected 4 times
  var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e)).ToList();

이것은 DB에서 2 개의 행을 반환 할 것입니다 (사용자의 고유 한 정렬 목록을 원하는 경우 정확할 수 있음).

그러나 대부분의 경우 정렬되지 않은 결과 목록을 원할 수 있습니다. 항상 SQL 쿼리에 대해 생각해야합니다. 진행 상황을 설명하려면 eshop 쇼핑 카트의 예를 참조하십시오.

  var priceListIDs = new int[1, 2, 2, 2, 2]; // user has bought 4 times item ID 2
  var shoppingCart = _dataContext.ShoppingCart
                     .Join(priceListIDs, sc => sc.PriceListID, pli => pli, (sc, pli) => sc)
                     .ToList();

그러면 DB에서 5 개의 결과 가 반환됩니다 . 이 경우 'contains'를 사용하는 것은 잘못된 것입니다.


13

간단해야합니다. 이 시도:

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.