[c#] Linq에서 Sql까지의 임의 행

조건이있을 때 Linq to SQL을 사용하여 임의의 행을 검색하는 가장 좋은 (그리고 가장 빠른) 방법은 무엇입니까 (예 : 일부 필드가 참이어야 함)?



답변

가짜 UDF를 사용하여 데이터베이스에서이를 수행 할 수 있습니다. 부분 클래스에서 데이터 컨텍스트에 메서드를 추가합니다.

partial class MyDataContext {
     [Function(Name="NEWID", IsComposable=true)]
     public Guid Random()
     { // to prove not used by our C# code... 
         throw new NotImplementedException();
     }
}

그럼 그냥 order by ctx.Random(); 이것은 SQL Server에서 임의의 순서를 지정합니다 NEWID(). 즉

var cust = (from row in ctx.Customers
           where row.IsActive // your filter
           orderby ctx.Random()
           select row).FirstOrDefault();

이것은 중소형 테이블에만 적합합니다. 거대한 테이블의 경우 서버 성능에 영향을 미치며 행 수 ( Count)를 찾은 다음 무작위로 하나를 선택 ( ) 하는 것이 더 효율적 Skip/First입니다.


카운트 접근 :

var qry = from row in ctx.Customers
          where row.IsActive
          select row;

int count = qry.Count(); // 1st round-trip
int index = new Random().Next(count);

Customer cust = qry.Skip(index).FirstOrDefault(); // 2nd round-trip


답변

Entity Framework의 또 다른 샘플 :

var customers = db.Customers
                  .Where(c => c.IsActive)
                  .OrderBy(c => Guid.NewGuid())
                  .FirstOrDefault();

이것은 LINQ to SQL에서 작동하지 않습니다. 는 OrderBy단순히 떨어졌다되고있다.


답변

편집 : 나는 이것이 LINQ to Objects가 아니라 LINQ to SQL이라는 것을 알았습니다. Marc의 코드를 사용하여 데이터베이스를 가져 오십시오. LINQ to Objects의 잠재적 인 관심 지점으로이 답변을 여기에 남겨 두었습니다.

이상하게도 실제로 카운트를 얻을 필요가 없습니다. 그러나 카운트를 얻지 않는 한 모든 요소를 ​​가져와야합니다.

당신이 할 수있는 것은 “현재”값과 현재 카운트의 아이디어를 유지하는 것입니다. 다음 값을 가져올 때 임의의 숫자를 가져 와서 “current”를 “new”확률로 1 / n으로 바꾸십시오. 여기서 n은 개수입니다.

따라서 첫 번째 값을 읽을 때 항상 “현재”값으로 만듭니다. 두 번째 값을 읽을 때 현재 값 (확률 1/2)으로 만들 수 있습니다 . 세 번째 값을 읽을 때 현재 값 (확률 1/3) 등으로 만들 수 있습니다 . 데이터가 부족한 경우 현재 값은 균일 한 확률로 읽은 모든 값 중 임의의 값입니다.

조건과 함께 적용하려면 조건을 충족하지 않는 것은 무시하십시오. 가장 쉬운 방법은 Where 절을 먼저 적용하여 시작할 “일치하는”시퀀스 만 고려하는 것입니다.

다음은 빠른 구현입니다. 괜찮은 것 같아요

public static T RandomElement<T>(this IEnumerable<T> source,
                                 Random rng)
{
    T current = default(T);
    int count = 0;
    foreach (T element in source)
    {
        count++;
        if (rng.Next(count) == 0)
        {
            current = element;
        }
    }
    if (count == 0)
    {
        throw new InvalidOperationException("Sequence was empty");
    }
    return current;
}


답변

효율적으로 달성하는 한 가지 방법 Shuffle은 각 레코드가 생성 될 때 임의의 정수로 채워진 데이터에 열을 추가하는 것입니다.

임의의 순서로 테이블에 액세스하는 부분 쿼리는 …

Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);

이것은 데이터베이스에서 XOR 연산을 수행하고 해당 XOR의 결과에 따라 정렬합니다.

장점 :-

  1. 효율성 : SQL이 순서를 처리하므로 전체 테이블을 가져올 필요가 없습니다.
  2. 반복 가능 : (테스트에 적합)-동일한 무작위 시드를 사용하여 동일한 무작위 순서를 생성 할 수 있습니다.

이것은 내 홈 오토메이션 시스템에서 재생 목록을 무작위로 만드는 데 사용하는 접근 방식입니다. 매일 새로운 시드를 선택하여 하루 동안 일관된 순서 (쉬운 일시 중지 / 재개 기능 허용)를 제공하지만 매일 각 재생 목록을 새롭게 살펴 봅니다.


답변

예를 들어 var count = 16테이블에서 임의의 행 을 얻으려면 다음과 같이 작성할 수 있습니다.

var rows = Table.OrderBy(t => Guid.NewGuid())
                        .Take(count);

여기에서는 EF를 사용했고 테이블은 Dbset입니다.


답변

임의의 행을 얻는 목적이 샘플링 인 경우 여기에서 매우 간략하게 이야기했습니다 . 에서 구체화 된 뷰를 사용하여 Sql Server 용 샘플링 프레임 워크를 개발 한 Microsoft Research 팀인 Larson 등의 멋진 접근 방식에 대해 설명했습니다. 실제 논문에 대한 링크도 있습니다.


답변

List<string> lst = new List<string>();
lst.Add("Apple");
lst.Add("Guva");
lst.Add("Graps");
lst.Add("PineApple");
lst.Add("Orange");
lst.Add("Mango");

var customers = lst.OrderBy(c => Guid.NewGuid()).FirstOrDefault();

설명 : guid (무작위)를 삽입하면 orderby가있는 순서가 무작위가됩니다.