私は、同僚が書いた、会社で使用している CRM アプリケーションとのインターフェイスとなるコードに取り組んでいます。このコードには、アプリケーションで何度も実行される 2 つの LINQ to Entities クエリがあり、そのうちの 1 つが非常に遅いため、それらを最適化するように依頼されました。
クエリは次のとおりです。
最初のクエリ、これはほぼ瞬時にコンパイルされます。CRM データベースから関係情報を取得し、アプリケーションによって指定された関係 ID のリストでフィルタリングします。
from relation in context.ADRELATION
where ((relationIds.Contains(relation.FIDADRELATION)) && (relation.FLDELETED != -1))
join addressTable in context.ADDRESS on relation.FIDADDRESS equals addressTable.FIDADDRESS
into temporaryAddressTable
from address in temporaryAddressTable.DefaultIfEmpty()
join mailAddressTable in context.ADDRESS on relation.FIDMAILADDRESS equals
mailAddressTable.FIDADDRESS into temporaryMailAddressTable
from mailAddress in temporaryMailAddressTable.DefaultIfEmpty()
select new { Relation = relation, Address = address, MailAddress = mailAddress };
2 番目のクエリは、コンパイルに約 4 ~ 5 秒かかり、データベースから人に関する情報を取得します (これも ID のリストによってフィルター処理されます)。
from role in context.ROLE
join relationTable in context.ADRELATION on role.FIDADRELATION equals relationTable.FIDADRELATION into temporaryRelationTable
from relation in temporaryRelationTable.DefaultIfEmpty()
join personTable in context.PERSON on role.FIDPERS equals personTable.FIDPERS into temporaryPersonTable
from person in temporaryPersonTable.DefaultIfEmpty()
join nationalityTable in context.TBNATION on person.FIDTBNATION equals nationalityTable.FIDTBNATION into temporaryNationalities
from nationality in temporaryNationalities.DefaultIfEmpty()
join titelTable in context.TBTITLE on person.FIDTBTITLE equals titelTable.FIDTBTITLE into temporaryTitles
from title in temporaryTitles.DefaultIfEmpty()
join suffixTable in context.TBSUFFIX on person.FIDTBSUFFIX equals suffixTable.FIDTBSUFFIX into temporarySuffixes
from suffix in temporarySuffixes.DefaultIfEmpty()
where ((rolIds.Contains(role.FIDROLE)) && (relation.FLDELETED != -1))
select new { Role = role, Person = person, relation = relation, Nationality = nationality, Title = title.FTXTBTITLE, Suffix = suffix.FTXTBSUFFIX };
SQL プロファイラーをセットアップし、両方のクエリから SQL を取得して、SQL Server Management Studio で実行しました。ID の数が多い (~1000) 場合でも、両方のクエリが非常に高速に実行されました。したがって、問題は LINQ クエリのコンパイルにあるようです。
コンパイルされたクエリを使用しようとしましたが、それらにはプリミティブ パラメーターのみを含めることができるため、フィルターを使用してその部分を取り除き、Invoke() 呼び出しの後にそれを適用する必要があったため、それが役立つかどうかはわかりません。また、このコードは WCF サービス操作で実行されるため、コンパイルされたクエリが後続の呼び出しでも存在するかどうかはわかりません。
最後に、2 番目のクエリで 1 つの列のみを選択することを試みました。これでは明らかに必要な情報が得られませんが、現在選択している約 200 列よりも高速であると考えました。そのような場合はありません。それでも 4 ~ 5 秒かかりました。
私は LINQ の第一人者ではないので、このコードをほとんどたどることができません (最適に書かれていないように感じますが、指を置くことはできません)。この問題が発生している理由について、誰かヒントを教えてもらえますか?
私が残した唯一の解決策は、これらすべてのテーブルを結合するのではなく、すべての情報を手動で選択することです。その後、約 5 ~ 6 件のクエリが発生します。それほど悪くはないと思いますが、ここではひどく非効率的な SQL (または少なくとも許容レベルの非効率性) を扱っていないので、それを防ぐことを望んでいました。
前もってありがとう、私が物事を明確にしたことを願っています。そうでない場合は、お気軽にお問い合わせください。詳細をお知らせします。
編集: エンティティフレームワークに関連付けを追加し(ターゲットデータベースには外部キーが指定されていませんでした)、クエリを次のように書き直しました:
context.ROLE.Where(role => rolIds.Contains(role.FIDROLE) && role.Relation.FLDELETED != -1)
.Select(role => new
{
ContactId = role.FIDROLE,
Person = role.Person,
Nationality = role.Person.Nationality.FTXTBNATION,
Title = role.Person.Title.FTXTBTITLE,
Suffix = role.Person.Suffix.FTXTBSUFFIX
});
はるかに読みやすく、高速でもあります。
提案をありがとう、私は間違いなく、さまざまな数の引数に対して複数のコンパイル済みクエリを作成することを念頭に置いています!