10

ページングされた結果を表示するために LINQ to Entities を使用しています。Skip()しかし、 、 、Take()およびOrderBy()呼び出しの組み合わせに問題があります。

OrderBy()割り当てが遅すぎることを除いて、すべて正常に機能します。Skip()結果セットがandによって切り捨てられた後に実行されTake()ます。

したがって、結果の各ページには順番に項目があります。ただし、セット全体を並べ替えてから と でそれらのレコードを制限するのではなく、一握りのページのデータに対して並べ替えが行われSkip()ますTake()

これらのステートメントに優先順位を設定するにはどうすればよいですか?

私の例(簡略化)

var query = ctx.EntitySet.Where(/* filter */).OrderByDescending(e => e.ChangedDate);
int total = query.Count();
var result = query.Skip(n).Take(x).ToList();

1つの可能な(しかし悪い)解決策

考えられる解決策の 1 つは、クラスター化インデックスを適用して列ごとに並べ替えることですが、この列は頻繁に変更されるため、挿入および更新時にデータベースのパフォーマンスが低下します。そして、私は本当にそれをしたくありません。

編集

ToTraceString()order by が結果セットに適用されるタイミングを実際に確認できるクエリを実行しました。残念ながら最後に。:(

SELECT 
-- columns
FROM  (SELECT 
    -- columns
    FROM   (SELECT -- columns
        FROM ( SELECT 
            -- columns
            FROM table1 AS Extent1
            WHERE  EXISTS (SELECT 
                -- single constant column
                FROM table2 AS Extent2
                WHERE (Extent1.ID = Extent2.ID) AND (Extent2.userId = :p__linq__4)
            )
        )  AS Project2
        limit 0,10  ) AS Limit1
    LEFT OUTER JOIN  (SELECT 
        -- columns
        FROM table2 AS Extent3 ) AS Project3 ON Limit1.ID = Project3.ID
UNION ALL
    SELECT 
    -- columns
    FROM   (SELECT -- columns
        FROM ( SELECT 
            -- columns
            FROM table1 AS Extent4
            WHERE  EXISTS (SELECT 
                -- single constant column
                FROM table2 AS Extent5
                WHERE (Extent4.ID = Extent5.ID) AND (Extent5.userId = :p__linq__4)
            )
        )  AS Project6
        limit 0,10  ) AS Limit2
    INNER JOIN table3 AS Extent6 ON Limit2.ID = Extent6.ID) AS UnionAll1
ORDER BY UnionAll1.ChangedDate DESC, UnionAll1.ID ASC, UnionAll1.C1 ASC
4

6 に答える 6

4

私の回避策

この問題を回避することができました。ここで誤解しないでください。優先順位の問題はまだ解決していませんが、軽減しました。

私がしたこと?

これは、Devartから回答を得るまで使用したコードです。彼らがこの問題を克服できない場合は、最後にこのコードを使用する必要があります。

// get ordered list of IDs
List<int> ids = ctx.MyEntitySet
    .Include(/* Related entity set that is needed in where clause */)
    .Where(/* filter */)
    .OrderByDescending(e => e.ChangedDate)
    .Select(e => e.Id)
    .ToList();

// get total count
int total = ids.Count;

if (total > 0)
{
    // get a single page of results
    List<MyEntity> result = ctx.MyEntitySet
        .Include(/* related entity set (as described above) */)
        .Include(/* additional entity set that's neede in end results */)
        .Where(string.Format("it.Id in {{{0}}}", string.Join(",", ids.ConvertAll(id => id.ToString()).Skip(pageSize * currentPageIndex).Take(pageSize).ToArray())))
        .OrderByDescending(e => e.ChangedOn)
        .ToList();
}

まず、エンティティの順序付き ID を取得しています。ID のみを取得すると、データ セットが大きくてもパフォーマンスが向上します。MySql クエリは非常にシンプルで、非常に優れたパフォーマンスを発揮します。2 番目の部分では、これらの ID を分割し、それらを使用して実際のエンティティ インスタンスを取得します。

考えてみると、クエリが単純化されているため、合計カウントの取得がはるかに高速になるため、これは最初に行った方法よりもさらに優れたパフォーマンスを発揮するはずです (私の質問で説明したように)。2 番目の部分は実際には非常によく似ていますが、私のエンティティは、Skipand Take...を使用してパーティション分割されるのではなく、ID によって返される点が異なります。

うまくいけば、誰かがこの解決策を役に立つと思うかもしれません.

于 2010-03-24T17:06:45.317 に答える
2

Linq to Entitiesと直接連携したことはありませんが、必要に応じて特定のストアドプロシージャを特定の場所にフックする方法が必要です。(Linq to SQLはそうしました。)そうであれば、このクエリをストアドプロシージャに変換して、必要なことを正確に実行し、効率的に実行できます。

于 2010-03-22T17:33:21.830 に答える
0

問題を説明するサンプルを作成し、私たちに送っていただけますか (support * devart * com、件名は「EF: Skip、Take、OrderBy」)。
私たちがあなたを助けることができることを願っています。フォーラムまたはお問い合わせフォーム
を使用してお問い合わせいただくこともできます。

于 2010-03-24T14:42:42.190 に答える
0

あなたのコメントから、リスト内の値を永続化することは受け入れられないと仮定します。

あなたが意図したように、反復を完全に最小限に抑える方法はありません(そして、私も試してみたので、希望を持って生きていました)。反復を 1 つ減らすとよいでしょう。カウントを一度取得してキャッシュ/セッションすることは可能ですか? 次に、次のことができます。

int total = ctx.EntitySet.Count;  // Hopefully you can not repeat doing this.
var result = ctx.EntitySet.Where(/* filter */).OrderBy(/* expression */).Skip(n).Take(x).ToList();

うまくいけば、カウントを何らかの方法でキャッシュするか、毎回必要としないようにすることができます。できなくても、これが一番です。

于 2010-03-22T16:46:28.097 に答える
0

注文がオフになっていることは確実ですか?SQL はどのように見えますか?

次のようにコードを並べ替えて、出力を投稿できますか?

// Redefine your queries. 
var query = ctx.EntitySet.Where(/* filter */).OrderBy(e => e.ChangedDate); 
var skipped = query.Skip(n).Take(x);

// let's look at the SQL, shall we?
var querySQL = query.ToTraceString();
var skippedSQL = skipped.ToTraceString();

// actual execution of the queries...
int total = query.Count(); 
var result = skipped.ToList(); 

編集:

私は絶対に確信しています。私の「編集」をチェックして、この場合に不可欠なスキップされたトレース結果を含むクエリのトレース結果を確認できます。カウントはそれほど重要ではありません。

ええ、私はそれを参照してください。うわー、それはスタンパーです。完全なバグでさえあるかもしれません。SQL Server を使用していないことに注意してください...どの DB を使用していますか? MySQlのようです。

于 2010-03-22T17:08:46.147 に答える
-1

一方通行:

var query = ctx.EntitySet.Where(/* filter */).OrderBy(/* expression */).ToList();
int total = query.Count;
var result = query.Skip(n).Take(x).ToList();

スキップする前にリストに変換してください。あまり効率的ではありません...

于 2010-03-22T16:28:57.740 に答える