4

私はPHPを使用してASP .NETコアにアップグレードします。Entity Frameworkコアの別のテーブルから最新のレコードを効率的に結合する


問題: T: {ID, Description, FK}States: {ID, ID_T, Time, State}:図について

、次の2つの表を想定。それらの間には1:nの関係があります(ID_T参照T.ID)。

私はStatesで関連する最新のレコード(もしあれば)と一緒に(1を言うことができます)FKのいくつかの特定の値でTからすべてのレコードを必要としています。それはのように記述することができるSQLの面では


SELECT T.ID, T.Description, COALESCE(s.State, 0) AS 'State' FROM T 
LEFT JOIN (
    SELECT ID_T, MAX(Time) AS 'Time' 
    FROM States 
    GROUP BY ID_T 
) AS sub ON T.ID = sub.ID_T 
LEFT JOIN States AS s ON T.ID = s.ID_T AND sub.Time = s.Time 
WHERE FK = 1 

私は、LINQでの効率的な同等のクエリ(または流暢API)を書くのに苦労しています。私がこれまで持っている最高の実用的なソリューションは、次のとおりです。

from t in _context.T 
where t.FK == 1 
join s in _context.States on t.ID equals o.ID_T into _s 
from s in _s.DefaultIfEmpty() 
let x = new 
{ 
    id = t.ID, 
    time = s == null ? null : (DateTime?)s.Time, 
    state = s == null ? false : s.State 
} 
group x by x.id into x 
select x.OrderByDescending(g => g.time).First(); 

それだけのようなものを実行するとき、私は出力ウィンドウに結果のSQLクエリをチェックすると:それはより多くの列を選択する

SELECT [t].[ID], [t].[Description], [t].[FK], [s].[ID], [s].[ID_T], [s].[Time], [s].[State] 
FROM [T] AS [t] 
LEFT JOIN [States] AS [s] ON [T].[ID] = [s].[ID_T] 
WHERE [t].[FK] = 1 
ORDER BY [t].[ID] 

だけでなく、私が必要とするよりも(実際のスキームにはもっと多くのものがあります)。 クエリーにグルーピングがないので、DBからすべてを選択すると思います(そして、Statesは巨大になります)、グルーピング/フィルタリングはDB外で行われています。


質問:

あなたは何をしますか?

  • LINQ/Fluent APIに効率的なクエリがありますか?
  • そうでない場合は、どのような回避策を使用できますか?
    • 生SQLは、特定のDB技術から抽象化の概念を遺跡とその使用は、現在のEntity Frameworkのコア(多分その最善の解決策)に非常に不格好です。
    • 私にとって、これはデータベースビューを使用する良い例のようです。エンティティフレームワークコアでサポートされていません(しかし、おそらくその最高のソリューションです)
+3

「効率性」と「エンティティフレームワーク」は合わない。 – nurdyguy

+3

EF Core group byはメモリ内で行われます。 EFコアの注文は、ストリーミングを許可するためにグループ化キーを生成し、グループ化しないで効率的なグループをサーバーに送信します。追跡に関する問題https://github.com/aspnet/EntityFramework/issues/2341 – Smit

+0

@Smit:ありがとうございます。私はすでにこの記事を見つけました:https://jonhilton.net/2016/11/09/is-entity-framework-core-production-ready/ 私はEF6に切り替えようとします –

答えて

2

OK。先読みthis article(ほぼ一歳)、元の質問や他の情報源へのSmitのコメントは、EFコアは実際にはまだ生産準備が整っていないようです。グループ化をSQLに変換することはできないため、クライアント側で実行されます(私の場合は重大な問題かもしれません)。これは観測された動作に対応します(生成されたSQLクエリはグループ化せず、すべてのグループのすべてを選択します)。 LINQクエリをLinqpadで試してみると、常に1つのSQLクエリに変換されます。

this articleに続いて、にEF6にダウングレードしました。私のモデルのコードといくつかのクエリにいくつかの変更が必要でした。元のLINQクエリで.First().FirstOrDefault()に変更した後、正常に動作し、必要な列のみを選択する単一のSQLクエリに変換されます。しかし、生成されたクエリは必要以上に複雑です。

NetMageの回答(小規模な修正後)のクエリを使用すると、元のSQLクエリとほとんど同じSQLクエリが発生します(COALESCEよりも複雑な構文があります)。 LINQでは

var latestState = from s in _context.States 
        group s by s.ID_T into sg 
        select new { ID = sg.Key, Time = sg.Time.Max() }; 

var ans = from t in _context.T 
      where t.FK == 1 
      join sub in latestState on t.ID equals sub.ID into subj 
      from sub in subj.DefaultIfEmpty() 
      join s in _context.States 
       on new { ID_T = t.ID, sub.Time } equals new { s.ID_T, s.Time } 
       into sj 
      from s in sj.DefaultIfEmpty() 
      select new { t.ID, t.Description, State = (s == null ? false : s.State) }; 

それは私の元のSQLクエリのようにエレガントではないのですが、意味的には同じだし、DB側の多かれ少なかれ同じことを行います。

EF6では、任意の生のSQLクエリとAFAIKをデータベースビューに使用する方がずっと便利です。

このアプローチの最大の欠点は、完全な.NETフレームワークをターゲットにする必要があることです.EF6は.NETコアと互換性がありません。

+1

私はちょうどマイクロソフトがそのような未完成の製品を出荷することが可能であるかどうか理解しておらず、それは問題ではないと思わせ、 –

+1

'State'の型はわかりませんでしたが、LINQクエリで' ?? 'を使ってSQLに変換することはできますが、残念なことに'?'はまだ翻訳されていません。私はそれを私の答えに加えました。 – NetMage

+0

@NetMage:ありがとう!月曜日にそれを試して、最終的に私の答えを更新します –

3

あなたはLINQに、よりまっすぐ翻訳を行うにしようとした場合はどうなりますか?

var latestState = from s in _context.States 
        group s by s.ID_T into sg 
        select new { ID_T = sg.Key, Time = sg.Time.Max() }; 

var ans = from t in _context.T 
      where t.FK == 1 
      join sub in latestState on t.ID equals sub.ID_T into subj 
      from sub in subj.DefaultIfEmpty() 
      join s in _context.States on new { t.ID, sub.Time } equals new { s.ID, s.Time } into sj 
      from s in sj.DefaultIfEmpty() 
      select new { t.ID, t.Description, State = (s == null ? 0 : s.State) }; 

あなたとselectを置き換えることができますので、どうやら??オペレータはCOALESCEに変換され、適切に空のテーブルを処理することができる:

  select new { t.ID, t.Description, State = s.State ?? 0 }; 
+0

ありがとう!私はすでに非常に似た(ほぼ同じ)クエリを試みました。結果はさらに悪くなります(3つのクエリを実行し、そのうちの1つでは、 'States'からすべてを選択します)。問題はグループ化にあり、EF CoreはまだそれをSQLに変換できず、クライアント側で実行します。私の更新された質問とSmitのコメントを参照してください。 –

+0

私の「ほぼ同じ」LINQクエリは、実際にはほとんど同じではありませんでした。あなたはより簡単で、より簡単なSQLクエリーになります。 2番目の結合条件には小さな間違いがあります( 's.ID'は' s.ID_T'でなければなりません - 私の答えを見てください)。それは私の元の問題を解決しないにもかかわらず+1。 –

+0

oh、 's.ID_Tによってグループsがあるべきです –

関連する問題