QueryExpression を使用してパフォーマンスを最適化する
この記事では、 QueryExpressionを使用してデータを取得するときにパフォーマンスを最適化する方法について説明します。
回避するパターン
Dataverse 用に最適化されたクエリを作成することは、アプリケーションが高速で応答性が高く、信頼性の高いエクスペリエンスを提供するために不可欠です。 このセクションでは、 RetrieveMultiple
メッセージ、または QueryBaseクラスから継承するパラメータを持つメッセージを使用して標準テーブルのクエリを作成するときに回避するパターンと理解しておくべき概念について説明します。 このガイダンスは、ODataを使用してレコードのコレクションに対してリクエストを送信する場合にも適用されます。 GET
ここでのガイダンスは、 Elasticテーブル や Dataverse 検索の使用時には適用されない可能性があります。
選択した列の数を最小限にする
クエリに必要のない列を含めないでください。 すべての列を返すクエリや多数の列を含むクエリでは、データセットのサイズやクエリの複雑さにより、パフォーマンスの問題が発生する可能性があります。
この方法は、特に 論理列に当てはまります。 論理列には、異なるデータベース テーブルに格納されている値が含まれます。 AttributeMetadata.IsLogicalプロパティ は、列が論理列であるかどうかを示します。 Dataverse には、他のデータベース テーブルからのデータを結合する必要があるため、多くの論理列を含むクエリは遅くなります。
フィルター条件の先頭にワイルド カードを使用しない
先頭にワイルドカード カード が付いた条件 (明示的に、または ends-with
のような オペレーター で暗黙的に) を使用するクエリは、パフォーマンスが低下する可能性があります。 先頭にワイルド カードを使用したクエリで Dataverse はデータベース インデックスを利用できないため、SQL はテーブル全体をスキャンすることになります。 結果セットを制限する他の非先行ワイルド カード クエリがある場合でも、テーブル スキャンが実行されることがあります。
次の例は、先頭にワイルドカード カード を使用するFetchXml 条件要素 です。
<condition attribute='accountnumber'
operator='like'
value='%234' />
次の例は、先頭にワイルドカード カード を使用する QueryExpressionConditionExpression です。
new ConditionExpression("accountnumber", ConditionOperator.Like, "%234")
次の例は、先頭にワイルドカード カード を使用するODataクエリです。
$filter=startswith(accountnumber,'%234')
クエリがタイムアウトし、このパターンが検出されると、Dataverse はこのパターンを使用しているクエリを識別するのに役立つ一意のエラーを返します。
名前:
LeadingWildcardCauseTimeout
コード:0x80048573
番号:-2147187341
メッセージ:The database operation timed out; this may be due to a leading wildcard value being used in a filter condition. Please consider removing filter conditions on leading wildcard values, as these filter conditions are expensive and may cause timeouts.
Dataverse は、組織の正常性に対するリスクとして特定された先頭のワイルドカード クエリを大幅に制限し、停止を防止します。 クエリスロットリングの詳細
先頭にワイルド カード クエリを使用している場合は、次のオプションをご検討ください。
- 代わりに Dataverse 検索 を使用してください。
- 先頭のワイルド カードを必要性を避けるため、データ モデルを変更します。
その他のワイルドカード文字
「 文字列値の条件でワイルドカード文字を使用する」で説明されているように、パーセント記号 ('%') 文字以外の文字はワイルドカードのように機能します。 先頭のワイルドカードのように動作する2つのクエリ文字列の例を次に示します。
_234%
[^a]234%
Dataverse これらの他の先頭のワイルドカード特殊文字で始まる検索文字列を含むクエリは大幅に制限されます。
ハイフン文字
データベース照合Unicodeソート規則により、ハイフン ('-') で始まる一部の検索文字列は先頭のワイルドカード検索のように実行されます。 ハイフンで始まる検索文字列では、検索文字列内の '%' 文字の前にワイルドカード以外の文字が含まれていない場合、データベース インデックスを利用できません。 たとえば、 -%
および -%234
はデータベース インデックスを効率的に使用できませんが、 -234%
は使用できます。 Dataverse ハイフンで始まる非効率的な検索文字列を大幅に制限します。 ハイフンのデータベース照合Unicodeソート規則の詳細については、 SQLサーバー照合 を参照してください。
フィルター条件で数式や計算列を使用しない
数式と計算列の値は、取得時にリアルタイムで計算されます。 これらの列にフィルターを使用するクエリは、フィルターを適用できるように、返される可能性のある各レコードの値を Dataverse に計算させます。 クエリが遅くなるのは、Dataverse は SQL を使用してこれらのクエリのパフォーマンスを向上させることができないためです。
クエリがタイムアウトし、このパターンが検出されると、Dataverse はこのパターンを使用しているクエリを識別するのに役立つ一意のエラーを返します。
名前:
ComputedColumnCauseTimeout
コード:0x80048574
番号:-2147187340
メッセージ:The database operation timed out; this may be due to a computed column being used in a filter condition. Please consider removing filter conditions on computed columns, as these filter conditions are expensive and may cause timeouts.
停止を防ぐために、Dataverse は、環境の正常性に対するリスクとして識別された計算列にフィルターを持つクエリを調整します。 クエリスロットリングの詳細
選択列別の並べ替えを避ける
FetchXml または QueryExpressionを使用する場合、選択列を使用してクエリ結果を並べ替えると、結果は各選択オプションのローカライズされたラベルを使用して並べ替えられます。 データベースに保存されている数値で並べ替えると、アプリケーションで良いエクスペリエンスが得られません。 選択列の順序付けには、ローカライズされたラベル値で行を結合して並べ替えるために、より多くのコンピューティング リソースが必要です。 この余分な作業により、クエリが遅くなります。 可能であれば、選択列の値で結果を並べ替えないようにしてください。
ヒント
ODataは異なります。 Dataverse Web API $orderby
を使用すると、ローカライズされたラベルではなく、選択列の整数値を使用して行を並べ替えます。
関連テーブル内の列別の順序付けを避ける
関連テーブルの列別に並べ替えると、複雑さが増すためクエリが遅くなります。
関連するテーブル別の並べ替えは、ここで説明するように必要な場合にのみ実行してください。
大規模なテキスト列での条件の使用を避ける
Dataverse には、大規模なテキスト文字列を格納できる 2 種類の列があります。
- StringAttributeMetadata には最大4,000文字を保存できます。
- MemoAttributeMetadata は、より大きな数値を保存できます。
これら両方の列の制限は、 MaxLength
プロパティを使用して指定されます。
850文字未満に設定された文字列列に対して条件を使用できます。 MaxLength
850を超えるすべてのメモ列または文字列列は、大きなテキスト列として定義されます。 MaxLength
Dataverse 大規模なテキスト列は、効果的にインデックスを作成するには大きすぎるため、フィルター条件に含めるとパフォーマンスが低下します。
Dataverse このような種類の列のデータをクエリするには、search の方が適しています。
クエリのヒント
重要
これらのオプションは、Microsoft テクニカル サポートによって推奨された場合にのみ適用してください。 これらのオプションを誤って使用すると、クエリのパフォーマンスが損なわれる可能性があります。
Microsoft SQL Server はクエリを最適化するための多くのクエリ ヒントをサポートします。 QueryExpression はクエリヒントをサポートしており、 QueryExpression.QueryHintsプロパティを使用してこれらのクエリオプションをSQLサーバーに渡すことができます。
クエリ オプション | SQL Server ヒント |
---|---|
ForceOrder |
強制命令 |
DisableRowGoal |
ヒント: DISABLE_OPTIMIZER_ROWGOAL |
EnableOptimizerHotfixes |
ヒント: ENABLE_QUERY_OPTIMIZER_HOTFIXES |
LoopJoin |
ループ結合 |
MergeJoin |
マージ結合 |
HashJoin |
ハッシュ結合 |
NO_PERFORMANCE_SPOOL |
パフォーマンススプールなし |
ENABLE_HIST_AMENDMENT_FOR_ASC_KEYS |
ヒント: ENABLE_HIST_AMENDMENT_FOR_ASC_KEYS |
詳細情報: ヒント (Transact-SQL) - クエリ
ノー ロック
以前のバージョンでは、 QueryExpression.NoLockプロパティ を使用してレコードの共有ロックを防止していました。 このプロパティを含める必要はなくなりました
ユニオンのヒント
異なるテーブルの列に を設定する FilterExpression を追加する場合は、 ConditionExpression
FilterExpression.FilterHintプロパティ を に設定することでパフォーマンスを向上させることができます。 union
ただし、いくつかの制限があります。
- FilterExpression.FilterOperator は LogicalOperator
.Or
を使用する必要があります。 - 各クエリには1つのヒントのみを含めることができます。
union
-
FilterExpression
ヒント付きのunion
フィルターが最上位フィルターにない場合は、 Dataverse クエリを変換し、union
ヒント付きのフィルターをルート フィルターに移動します。 -
union
ヒントが3レベルを超える場合、無視されます。
次の例では、 union
アカウント telephone1
テーブルと 連絡先 テーブルの両方の列に ヒントを含むフィルターを設定します。
QueryExpression query = new("email")
{
ColumnSet = new ColumnSet("activityid", "subject"),
Criteria = new FilterExpression(LogicalOperator.And)
{
Conditions = {
{
new ConditionExpression(
attributeName:"subject",
conditionOperator:ConditionOperator.Like,
value: "Alert:%")
},
{
new ConditionExpression(
attributeName:"statecode",
conditionOperator:ConditionOperator.Equal,
value: 0)
}
},
Filters = {
{
new FilterExpression(LogicalOperator.Or){
FilterHint = "union",
Conditions = {
{
new ConditionExpression(
attributeName:"telephone1",
conditionOperator:ConditionOperator.Equal,
value: "555-123-4567"){
EntityName = "ac"
}
},
{
new ConditionExpression(
attributeName:"telephone1",
conditionOperator:ConditionOperator.Equal,
value: "555-123-4567"){
EntityName = "co"
}
}
}
}
}
}
}
};
LinkEntity linkToAccount = query.AddLink(
linkToEntityName: "account",
linkFromAttributeName: "regardingobjectid",
linkToAttributeName: "accountid",
joinOperator: JoinOperator.LeftOuter);
linkToAccount.EntityAlias = "ac";
LinkEntity linkToContact = query.AddLink(
linkToEntityName: "contact",
linkFromAttributeName: "regardingobjectid",
linkToAttributeName: "contactid",
joinOperator: JoinOperator.LeftOuter);
linkToContact.EntityAlias = "co";
参照
QueryExpressionを使用してデータをクエリする
選択 列 (QueryExpressionを使用)
QueryExpressionを使用してテーブルを結合する
QueryExpressionを使用して行を並べ替える
QueryExpressionを使用して行をフィルタリングする
QueryExpressionを使用したページ結果
QueryExpressionを使用してデータを集計する
QueryExpressionを使用して行をカウントする