조건이있을 때 Linq to 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;
}
current
것이다 항상 첫 번째 요소로 설정 될 수있다. 두 번째 반복에서 두 번째 요소로 설정되는 50 % 변경이 있습니다. 세 번째 반복에서는 33 %의 확률로 세 번째 요소로 설정됩니다. break 문을 추가하면 첫 번째 요소를 읽은 후 항상 종료하여 전혀 무작위가 아닙니다.
효율적으로 달성하는 한 가지 방법 Shuffle
은 각 레코드가 생성 될 때 임의의 정수로 채워진 데이터에 열을 추가하는 것입니다.
임의의 순서로 테이블에 액세스하는 부분 쿼리는 ...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
이것은 데이터베이스에서 XOR 연산을 수행하고 해당 XOR의 결과에 따라 정렬합니다.
장점 :-
이것은 내 홈 오토메이션 시스템에서 재생 목록을 무작위로 만드는 데 사용하는 접근 방식입니다. 매일 새로운 시드를 선택하여 하루 동안 일관된 순서 (쉬운 일시 중지 / 재개 기능 허용)를 제공하지만 매일 각 재생 목록을 새롭게 살펴 봅니다.
result = result.OrderBy(s => s.Shuffle ^ seed);
(즉 ~, & 및 | 연산자를 통해 XOR을 구현할 필요가 없음).
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가있는 순서가 무작위가됩니다.
적은 수의 무작위 페이지를 가져 오는 방법을 궁금해하며 각 사용자는 다른 무작위 3 페이지를 얻습니다.
이것은 Sharepoint 2010의 페이지 목록에 대해 LINQ를 사용하여 쿼리하는 최종 솔루션입니다. Visual Basic에 있습니다. 죄송합니다.
Dim Aleatorio As New Random()
Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3
아마도 많은 수의 결과를 쿼리하기 전에 몇 가지 프로파일 링을 받아야하지만 제 목적에는 완벽합니다
아래 예제에서는 소스를 호출하여 개수를 검색 한 다음 0에서 n 사이의 숫자를 사용하여 소스에 스킵 표현식을 적용합니다. 두 번째 방법은 임의의 개체 (메모리의 모든 항목을 정렬 함)를 사용하여 순서를 적용하고 메서드 호출에 전달 된 번호를 선택합니다.
public static class IEnumerable
{
static Random rng = new Random((int)DateTime.Now.Ticks);
public static T RandomElement<T>(this IEnumerable<T> source)
{
T current = default(T);
int c = source.Count();
int r = rng.Next(c);
current = source.Skip(r).First();
return current;
}
public static IEnumerable<T> RandomElements<T>(this IEnumerable<T> source, int number)
{
return source.OrderBy(r => rng.Next()).Take(number);
}
}
나는이 방법을 사용하여 임의의 뉴스를 가져오고 잘 작동합니다.)
public string LoadRandomNews(int maxNews)
{
string temp = "";
using (var db = new DataClassesDataContext())
{
var newsCount = (from p in db.Tbl_DynamicContents
where p.TimeFoPublish.Value.Date <= DateTime.Now
select p).Count();
int i;
if (newsCount < maxNews)
i = newsCount;
else i = maxNews;
var r = new Random();
var lastNumber = new List<int>();
for (; i > 0; i--)
{
int currentNumber = r.Next(0, newsCount);
if (!lastNumber.Contains(currentNumber))
{ lastNumber.Add(currentNumber); }
else
{
while (true)
{
currentNumber = r.Next(0, newsCount);
if (!lastNumber.Contains(currentNumber))
{
lastNumber.Add(currentNumber);
break;
}
}
}
if (currentNumber == newsCount)
currentNumber--;
var news = (from p in db.Tbl_DynamicContents
orderby p.ID descending
where p.TimeFoPublish.Value.Date <= DateTime.Now
select p).Skip(currentNumber).Take(1).Single();
temp +=
string.Format("<div class=\"divRandomNews\"><img src=\"files/1364193007_news.png\" class=\"randomNewsImg\" />" +
"<a class=\"randomNews\" href=\"News.aspx?id={0}\" target=\"_blank\">{1}</a></div>",
news.ID, news.Title);
}
}
return temp;
}
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);
임의의 2 개 행 선택
Marc Gravell의 솔루션에 추가합니다. datacontext 클래스 자체로 작업하지 않는 경우 (예 : 테스트 목적으로 데이터 컨텍스트를 위조하기 위해 프록시를 사용하기 때문에) 정의 된 UDF를 직접 사용할 수 없습니다. 실제 데이터 컨텍스트 클래스의 하위 클래스 또는 부분 클래스.
이 문제에 대한 해결 방법은 프록시에 Randomize 함수를 만들어 무작위화할 쿼리를 제공하는 것입니다.
public class DataContextProxy : IDataContext
{
private readonly DataContext _context;
public DataContextProxy(DataContext context)
{
_context = context;
}
// Snipped irrelevant code
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => _context.Random());
}
}
코드에서 사용하는 방법은 다음과 같습니다.
var query = _dc.Repository<SomeEntity>();
query = _dc.Randomize(query);
완료하려면 다음은 FAKE 데이터 컨텍스트 (메모리 엔티티에서 사용)에서이를 구현하는 방법입니다.
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => Guid.NewGuid());
}