조건이있을 때 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의 결과에 따라 정렬합니다.
장점 :-
- 효율성 : SQL이 순서를 처리하므로 전체 테이블을 가져올 필요가 없습니다.
- 반복 가능 : (테스트에 적합)-동일한 무작위 시드를 사용하여 동일한 무작위 순서를 생성 할 수 있습니다.
이것은 내 홈 오토메이션 시스템에서 재생 목록을 무작위로 만드는 데 사용하는 접근 방식입니다. 매일 새로운 시드를 선택하여 하루 동안 일관된 순서 (쉬운 일시 중지 / 재개 기능 허용)를 제공하지만 매일 각 재생 목록을 새롭게 살펴 봅니다.
답변
예를 들어 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가있는 순서가 무작위가됩니다.