web-dev-qa-db-ja.com

LinqからSQLへのランダムな行

私が条件を持っているときにLinq to SQLを使用してランダムな行を取得する最良の(そして最も速い)方法は何ですか?一部のフィールドは真でなければなりませんか?

110
Julien Poulin

偽の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
_
167
Marc Gravell

Entity Frameworkの別のサンプル:

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

これは、LINQ to SQLでは機能しません。 OrderByは単に削除されています。

58

編集:私はこれが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;
}
31
Jon Skeet

効率的に達成する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. 効率的:SQLは順序付けを処理し、テーブル全体をフェッチする必要はありません
  2. 反復可能:(テストに適しています)-同じランダムシードを使用して同じランダムな順序を生成できます

これは、プレイリストをランダム化するために私のホームオートメーションシステムで使用されるアプローチです。毎日新しいシードを選択して、1日中一貫した順序を提供します(簡単な一時停止/再開機能を許可します)が、毎日新しいプレイリストを新しく見ます。

19
Ian Mercer

あなたが取得したい場合var count = 16テーブルからランダムな行、あなたは書くことができます

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

ここではE.Fを使用しましたが、テーブルはDbsetです

7
Artur Keyan

ランダムな行を取得する目的がサンプリングである場合、私は非常に簡単に話しました こちら Larsonら、Microsoft Researchチームがマテリアライズドビューを使用してSql Serverのサンプリングフレームワークを開発したNiceアプローチについて。実際の論文へのリンクもあります。

1
naiemk
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の順序はランダムになります。

1
Nayeem Mansoori

私はランダムなニュースとその仕事をうまく取るためにこの方法を使用します;)

    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;
    }
0
sadati

LINQPadを使用する場合は、C#プログラムモードに切り替えて、次のようにします。

void Main()
{
    YourTable.OrderBy(v => Random()).FirstOrDefault.Dump();
}

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}
0
alexey
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);

ランダムな2行を選択

0
Bahadır ATASOY

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());
}
0
Dave de Jong

ここで、少数のページからいくつかのランダムページを取得する方法を知りたいので、各ユーザーがいくつかの異なるランダム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

おそらく、多数の結果を照会する前にプロファイリングを取得する必要がありますが、それは私の目的には最適です

0
Fran

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()
0
JCO

DataTablesに対するランダム関数クエリがあります。

var result = (from result in dt.AsEnumerable()
              order by Guid.NewGuid()
              select result).Take(3); 
0
midhun sankar

次の例では、ソースを呼び出してカウントを取得し、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);
    }
}
0
user1619860