私が条件を持っているときに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()
;これは、NEWID()
の好意により、SQLサーバーでランダムな順序付けを行います。つまり.
_var cust = (from row in ctx.Customers
where row.IsActive // your filter
orderby ctx.Random()
select row).FirstOrDefault();
_
これは、中小規模のテーブルにのみ適していることに注意してください。巨大なテーブルの場合、サーバーのパフォーマンスに影響があり、行数(Count
)を見つけてから、ランダムに1つ(_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 SQLではなくLINQ to SQLであることに気付いただけです。 Marcのコードを使用して、データベースにこれを実行させます。 LINQ to Objectsの潜在的な関心点として、この回答をここに残しました。
奇妙なことに、実際にカウントを取得する必要はありません。ただし、カウントを取得しない限り、すべての要素を取得する必要があります。
できることは、「現在の」値と現在のカウントの概念を保持することです。次の値を取得するとき、乱数を取得し、「現在」を「新しい」に1/nの確率で置き換えます(nはカウント)。
したがって、最初の値を読み取るとき、alwaysを「現在の」値にします。 2番目の値を読み取るとき、mightを現在の値(確率1/2)にします。 3番目の値を読み取るとき、mightは現在の値(確率1/3)などになります。データがなくなると、現在の値はすべてのうちのランダムな値になります。一律の確率で読みます。
条件にそれを適用するには、条件を満たさないものは無視してください。これを行う最も簡単な方法は、最初にWhere句を適用することで、最初に「一致する」シーケンスのみを考慮することです。
簡単な実装を次に示します。 I 思考大丈夫...
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;
}
効率的に達成する1つの方法は、データShuffle
に列を追加することです。この列には、ランダムなintが設定されます(各レコードの作成時に)。
ランダムな順序でテーブルにアクセスする部分クエリは...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
これは、データベースでXOR操作を実行し、そのXORの結果によって順序付けします。
利点:-
これは、プレイリストをランダム化するために私のホームオートメーションシステムで使用されるアプローチです。毎日新しいシードを選択して、1日中一貫した順序を提供します(簡単な一時停止/再開機能を許可します)が、毎日新しいプレイリストを新しく見ます。
あなたが取得したい場合var count = 16
テーブルからランダムな行、あなたは書くことができます
var rows = Table.OrderBy(t => Guid.NewGuid())
.Take(count);
ここではE.Fを使用しましたが、テーブルはDbsetです
ランダムな行を取得する目的がサンプリングである場合、私は非常に簡単に話しました こちら Larsonら、Microsoft Researchチームがマテリアライズドビューを使用してSql Serverのサンプリングフレームワークを開発したNiceアプローチについて。実際の論文へのリンクもあります。
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の順序はランダムになります。
私はランダムなニュースとその仕事をうまく取るためにこの方法を使用します;)
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;
}
LINQPadを使用する場合は、C#プログラムモードに切り替えて、次のようにします。
void Main()
{
YourTable.OrderBy(v => Random()).FirstOrDefault.Dump();
}
[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
throw new NotImplementedException();
}
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);
ランダムな2行を選択
Marc Gravellのソリューションに追加します。 datacontextクラス自体を使用していない場合(たとえば、テスト目的でdatacontextを偽装するなど)、定義済みのUDFを直接使用することはできません。実際のデータコンテキストクラスのサブクラスまたは部分クラス。
この問題の回避策は、プロキシにランダム化関数を作成し、ランダム化するクエリをそれに与えることです。
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());
}
ここで、少数のページからいくつかのランダムページを取得する方法を知りたいので、各ユーザーがいくつかの異なるランダム3ページを取得します。
これが私の最終的な解決策であり、Sharepoint 2010のページのリストに対してLINQでクエリを実行します。VisualBasicにあります。申し訳ありませんが:p
Dim Aleatorio As New Random()
Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3
おそらく、多数の結果を照会する前にプロファイリングを取得する必要がありますが、それは私の目的には最適です
C#ステートメントのようにLINQPadでLINQ to SQLを使用する
IEnumerable<Customer> customers = this.ExecuteQuery<Customer>(@"SELECT top 10 * from [Customers] order by newid()");
customers.Dump();
生成されたSQLは
SELECT top 10 * from [Customers] order by newid()
DataTable
sに対するランダム関数クエリがあります。
var result = (from result in dt.AsEnumerable()
order by Guid.NewGuid()
select result).Take(3);
次の例では、ソースを呼び出してカウントを取得し、0〜nの数値を使用してソースにスキップ式を適用します。 2番目のメソッドは、ランダムオブジェクト(メモリ内のすべてを順序付けする)を使用して順序を適用し、メソッド呼び出しに渡される番号を選択します。
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);
}
}