40

一致するエンティティをランダムな順序で返すにはどうすればよいですか?
明確にするために、これはEntity FrameworkのものとLINQ to Entitiesです。

(航空コード)

IEnumerable<MyEntity> results = from en in context.MyEntity
                                where en.type == myTypeVar
                                orderby ?????
                                select en;

ありがとう

編集:
これをコンテキストに追加しようとしました:

public Guid Random()
{
    return new Guid();
}

そして、このクエリを使用して:

IEnumerable<MyEntity> results = from en in context.MyEntity
                                where en.type == myTypeVar
                                orderby context.Random()
                                select en;

しかし、私はこのエラーが発生しました:

System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Guid Random()' method, and this method cannot be translated into a store expression..

編集(現在のコード):

IEnumerable<MyEntity> results = (from en in context.MyEntity
                                 where en.type == myTypeVar
                                 orderby context.Random()
                                 select en).AsEnumerable();
4

12 に答える 12

53

これを行う簡単な方法はGuid.NewGuid()順序付けですが、順序付けはクライアント側で行われます。サーバー側で何かランダムなことをするように EF を説得できるかもしれませんが、それは必ずしも単純ではありませ

EF ではなく .NET 側で順序付けを行うには、次のものが必要ですAsEnumerable

IEnumerable<MyEntity> results = context.MyEntity
                                       .Where(en => en.type == myTypeVar)
                                       .AsEnumerable()
                                       .OrderBy(en => context.Random());

リストで順序付けされていないバージョンを取得してから、それをシャッフルする方がよいでしょう。

Random rnd = ...; // Assume a suitable Random instance
List<MyEntity> results = context.MyEntity
                                .Where(en => en.type == myTypeVar)
                                .ToList();

results.Shuffle(rnd); // Assuming an extension method on List<T>

シャッフルは、他のことを除けば、ソートよりも効率的です。ただし、適切なインスタンスを取得する方法の詳細については、ランダム性に関する私の記事を参照してください。RandomStack Overflow で利用できる Fisher-Yates シャッフルの実装が多数あります。

于 2009-03-17T16:08:01.097 に答える
39

Jon's answer is helpful, but actually you can have the DB do the ordering using Guid and Linq to Entities (at least, you can in EF4):

from e in MyEntities
orderby Guid.NewGuid()
select e

This generates SQL resembling:

SELECT
[Project1].[Id] AS [Id], 
[Project1].[Column1] AS [Column1]
FROM ( SELECT 
    NEWID() AS [C1],                     -- Guid created here
    [Extent1].[Id] AS [Id], 
    [Extent1].[Column1] AS [Column1],
    FROM [dbo].[MyEntities] AS [Extent1]
)  AS [Project1]
ORDER BY [Project1].[C1] ASC             -- Used for sorting here

In my testing, using Take(10) on the resulting query (converts to TOP 10 in SQL), the query ran consistently between 0.42 and 0.46 sec against a table with 1,794,785 rows. No idea whether SQL Server does any kind of optimisation on this or whether it generated a GUID for every row in that table. Either way, that would be considerably faster than bringing all those rows into my process and trying to sort them there.

于 2010-11-07T22:49:26.453 に答える
30

簡単な解決策は、配列 (またはList<T>) を作成し、そのインデックスをランダム化することです。

編集:

static IEnumerable<T> Randomize<T>(this IEnumerable<T> source) {
  var array = source.ToArray();
  // randomize indexes (several approaches are possible)
  return array;
}

EDIT:個人的には、Jon Skeetの答えがよりエレガントだと思います:

var results = from ... in ... where ... orderby Guid.NewGuid() select ...

確かに、 の代わりに乱数ジェネレーターを使用できますGuid.NewGuid()

于 2009-03-17T16:09:38.173 に答える
4

NewGuid残念ながら、サーバー側でソートするためのハックにより、結合 (または熱心なフェッチが含まれる) の場合にエンティティが複製されます。

この問題については、この質問を参照してください。

この問題を克服するには、サーバー側で一意の値を計算NewGuidする SQLの代わりに、checksumクライアント側で一度計算されたランダム シードを使用してランダム化することができます。以前にリンクされた質問に対する私の回答を参照してください。

于 2015-07-06T12:31:37.850 に答える
1

lolo_house には、本当にきちんとしたシンプルで汎用的なソリューションがあります。コードを別の静的クラスに配置して機能させるだけです。

using System;
using System.Collections.Generic;
using System.Linq;

namespace SpanishDrills.Utilities
{
    public static class LinqHelper
    {
        public static IEnumerable<T> Randomize<T>(this IEnumerable<T> pCol)
        {
            List<T> lResultado = new List<T>();
            List<T> lLista = pCol.ToList();
            Random lRandom = new Random();
            int lintPos = 0;

            while (lLista.Count > 0)
            {
                lintPos = lRandom.Next(lLista.Count);
                lResultado.Add(lLista[lintPos]);
                lLista.RemoveAt(lintPos);
            }

            return lResultado;
        }
    }
}

次に、コードを使用するには、次のようにします。

var randomizeQuery = Query.Randomize();

とても簡単!lolo_houseさん、ありがとうございます。

于 2014-06-26T05:56:13.987 に答える
0

( EF Code Firstからの相互投稿: How to get random rows )

2 つのオプションの比較:


Skip(乱数行)

方法

private T getRandomEntity<T>(IGenericRepository<T> repo) where T : EntityWithPk<Guid> {
    var skip = (int)(rand.NextDouble() * repo.Items.Count());
    return repo.Items.OrderBy(o => o.ID).Skip(skip).Take(1).First();
}
  • 2クエリかかります

生成された SQL

SELECT [GroupBy1].[A1] AS [C1]
FROM   (SELECT COUNT(1) AS [A1]
        FROM   [dbo].[People] AS [Extent1]) AS [GroupBy1];

SELECT TOP (1) [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT [Extent1].[ID]                                  AS [ID],
               [Extent1].[Name]                                AS [Name],
               [Extent1].[Age]                                 AS [Age],
               [Extent1].[FavoriteColor]                       AS [FavoriteColor],
               row_number() OVER (ORDER BY [Extent1].[ID] ASC) AS [row_number]
        FROM   [dbo].[People] AS [Extent1]) AS [Extent1]
WHERE  [Extent1].[row_number] > 15
ORDER  BY [Extent1].[ID] ASC;

ガイド

方法

private T getRandomEntityInPlace<T>(IGenericRepository<T> repo) {
    return repo.Items.OrderBy(o => Guid.NewGuid()).First();
}

生成された SQL

SELECT TOP (1) [Project1].[ID]            AS [ID],
               [Project1].[Name]          AS [Name],
               [Project1].[Age]           AS [Age],
               [Project1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT NEWID()                   AS [C1],
               [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
        FROM   [dbo].[People] AS [Extent1]) AS [Project1]
ORDER  BY [Project1].[C1] ASC

したがって、新しい EF では、それがNewGuidSQL に変換されていることを再度確認できます (@DrewNoakes https://stackoverflow.com/a/4120132/1037948で確認)。どちらも「in-sql」メソッドですが、Guid バージョンの方が速いと思いますか? スキップするために並べ替える必要がなく、スキップする量を合理的に推測できる場合は、Skip メソッドの方が適している可能性があります。

于 2013-06-14T16:29:36.983 に答える
0

これを行うための良い方法があります(主にグーグルの人々向け)。

最後に .Take(n) を追加して、セット番号のみを取得することもできます。

model.CreateQuery<MyEntity>(   
    @"select value source.entity  
      from (select entity, SqlServer.NewID() as rand  
            from Products as entity 
            where entity.type == myTypeVar) as source  
            order by source.rand");
于 2010-10-07T01:09:41.843 に答える
0

トロの答えは私が使用するものですが、むしろ次のようになります。

static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
  var list = source.ToList();
  var newList = new List<T>();

  while (source.Count > 0)
  {
     //choose random one and MOVE it from list to newList
  }

  return newList;
}
于 2009-03-17T17:07:33.353 に答える
-1

理論的に言えば(実際にはまだ試していません)、次のようにしてうまくいくはずです:

コンテキスト クラスに部分クラスを追加します。

public partial class MyDataContext{

        [Function(Name = "NEWID", IsComposable = true)] 
        public Guid Random()
        { 
            // you can put anything you want here, it makes no difference 
            throw new NotImplementedException();
        }
}

実装 :

from t in context.MyTable
orderby  context.Random()
select t; 
于 2013-01-01T08:02:08.400 に答える