レガシ アプリケーションで多くのn+1 の問題を回避するために、いくつかのユーティリティ メソッドを追加しようと考えています。
一般的なパターンは次のとおりです。
select a.* /* over 10 columns */
from [table-A] a
where /* something */
ClassA
レコード インスタンスのコレクションに取得されます
次に、サブインスタンスが遅延取得されます。
select b.* /* over 10 columns */
from [sub-table-B] b
where b.ParentId = @ClassA_ID
これにより、n+1 選択の問題が発生します。ほとんどの場合、ヒットする頻度の低いページで 2 つのインスタンスしか取得されないため、これは大きな問題ではありませClassA
んが、アプリケーションのスケーリングに伴い、この n+1 の問題によりページが遅くなりすぎる場所が増えています。
ClassA
インスタンスとClassB
インスタンスが一緒に取得されるように、このアプリケーションの既存のデータ アクセス コードの一部を置き換えようとしています。
これを行うには、次の 3 つの方法があると思います。
1)ClassA
今と同じようにインスタンスを取得してからClassB
、1 回の集約呼び出しでインスタンスを取得します。
select b.*
from [sub-table-B] b
where b.ParentId in ( /* list of parent IDs */ )
これは 2 つの別個の DB 呼び出しであり、動的 SQL のクエリ プランはキャッシュできません (ID のリストのため)。
2) サブクエリでClassB
インスタンスを取得します。
select b.*
from [sub-table-B] b
inner join [table-A] a
on b.ParentId = a.[ID]
where /* something */
これも 2 つの DB 呼び出しであり、クエリに対するクエリ[table-A]
は 2 回評価する必要があります。
3) すべてをまとめて、ClassA
インスタンスの重複を排除します。
select a.*, b.*
from [table-A] a
left outer join [sub-table-B] b
on a.[ID] = b.ParentId
where /* something */
これは 1 回の DB 呼び出しにすぎませんが、内容が[table-A]
繰り返されるようになりました。結果セットが大きくなり、DB からクライアントにデータを送信する時間が長くなります。
したがって、実際にはこれは 3 つの可能な妥協点です。
- 2 つの DB 呼び出し、クエリ キャッシュなし
- 2 つの DB 呼び出し、複雑なクエリが 2 回評価される
- 1 DB 呼び出し、非常に大きな結果セット
これらの 3 つのパターンは、任意の 1 つの親子ペアのテーブルに対してテストできますが、大量のテーブルがあります。私が知りたいのは、一貫してどのパターンが速いかということです。もっと重要なのはなぜですか?これらの妥協の 1 つは、明らかなパフォーマンスの低下要因ですか?
Linq、EF、NHibernate などの既存のメカニズムは何を使用していますか?
3つすべてよりも優れた4番目の方法はありますか?