web-dev-qa-db-ja.com

EntityFrameworkのRo​​w_numberover(yyyによるパーティション)

EFを使用してパーティションを介してRow_numberでデータをロードしたい。

    SELECT *
    FROM (
       SELECT sf.SerialFlowsId
                     ,sf.GoodsSerialId
                     ,d.FormTypeId
                     , d.GoodsId
                     ,ROW_NUMBER() OVER (PARTITION BY d.GoodsId, sf.GoodsSerialId ORDER BY sf.Date DESC)row
       FROM sam.SerialFlows sf
       INNER JOIN sam.Detail d ON d.DetailId = sf.DetailId
       )z
WHERE z.row =1 
       AND z.FormTypeId=7
       AND z.GoodsId=51532

このクエリは私の期待です。

この式を使おうとしていますが、残念ながらZip拡張メソッドがefで認識されません

var goodsSerials = context.SerialFlows.OrderByDescending(x => x.Date).GroupBy(x => new { x.Detail.GoodsID, x.Date })
                    .Select(g => new {g})
                    .SelectMany(z => z.g.Select(c => c)).Zip(m, (j, i) => new { GoodSerial=j,j.Detail.FormTypeID,j.Detail.GoodsID,rn=i })
                    .Where(x => x.rn== 1 && x.GoodsID== goodsId && x.FormTypeID==7).Select(x => x.GoodSerial).ToList();

SerialFlowsテーブルに20000000を超えるレコードがあります。

**編集済み

  var goodsSerials = context.SerialFlows
                                          .Where(e => e.Detail.GoodsID == goodsId )
                                          .GroupBy(x => x.GoodsSerialID)
                                          .Select(g => g.OrderByDescending(e=>e.Date).Take(1))
                                          .SelectMany(e => e.Where(x=>x.Detail.FormTypeID==7).Select(z=>z.GoodsSerial)).ToList();

***このクエリで解決

        var goodsSerials = context.SerialFlows
                                          .Include(x => x.Detail)
                                          .Where(e => e.Detail.GoodsID == goodsId)
                                          .GroupBy(x => x.GoodsSerialID)
                                          .Select(g => g.OrderByDescending(e => e.Date).Take(1).Where(x=>x.Detail.FormTypeID==7))
                                          .SelectMany(e => e.Select(z => z.GoodsSerial)).ToList();
16
Mahdi Farhani

SQLクエリから、最初にPARTITION BY句の内容ですべてをグループ化し、各グループを日付で並べ替える必要があると思います。次に、各グループを投影して、各エントリをそのインデックスに含めます。次に、SelectManyを使用してすべてのグループをフラット化し、フィルターを適用して、最後に目的の結果を投影します。いわゆるZipはまったく必要ないことがわかります。

Edit:行番号でフィルタリングする必要があるが、Expression<Func<T,int,TResult>>を受け入れるSelectメソッドのように見えるためサポートされていません(Linq ToEntityのZipメソッドも同様です)。式ツリーを構築することの問題ではないと思います。つまり、手動で構築しても、サポートされません。代わりにSkipTakeを使用して、必要な行をフィルタリングできる回避策を使用できると思います。

次のコードは、各グループの最初の行のみをフィルタリングします(条件rn == 1と同等)。

var goodsSerials = context.SerialFlows
                          .Where(e => e.Detail.GoodsID == goodsId &&
                                      e.Detail.FormTypeID == 7)
                          .GroupBy(x => new { x.Detail.GoodsID, x.GoodsSerialId })
                          .Select(g => g.OrderByDescending(e => e.Date)
                                        .Take(1))
                          .SelectMany(e => e).ToList();

WhereGoodsIDの1つの値のみをフィルタリングするため、GroupByはキーにGoodsIDを含める必要がないため、次のように簡単になります。 :

var goodsSerials = context.SerialFlows
                          .Where(e => e.Detail.GoodsID == goodsId &&
                                      e.Detail.FormTypeID == 7)
                          .GroupBy(x => x.GoodsSerialId)
                          .Select(g => g.OrderByDescending(e => e.Date).Take(1))
                          .SelectMany(e => e).ToList();

SkipTakeを使用してさまざまな場合に適用するという考えを理解していただければ幸いです。

14
King King