QueryExpression を使用してパフォーマンスを最適化する

この記事では、QueryExpression を使用してデータを取得するときにパフォーマンスを最適化する方法について説明します。

回避するパターン

Dataverse 用に最適化されたクエリを作成することは、アプリケーションが高速で応答性が高く、信頼性の高いエクスペリエンスを提供するために不可欠です。 このセクションでは、RetrieveMultiple メッセージ、または QueryBase クラスから継承するパラメータを持つメッセージを使用して、標準テーブルのクエリを作成するときに回避するパターンと理解しておくべき概念について説明します。 このガイダンスは、ODataを使用してレコードのコレクションに対してリクエストを送信する場合にも適用されます。 GET ここでのガイダンスは、エラスティック テーブルDataverse 検索を使用する場合には適用されない場合があります。

選択した列の数を最小限にする

クエリに必要のない列を含めないでください。 すべての列を返すクエリや多数の列を含むクエリでは、データセットのサイズやクエリの複雑さにより、パフォーマンスの問題が発生する可能性があります。

この方法は、特に論理列に当てはまります。 論理列には、異なるデータベース テーブルに格納されている値が含まれます。 AttributeMetadata.IsLogical プロパティ は、列が論理列であるかどうかを示します。 Dataverse には、他のデータベース テーブルからのデータを結合する必要があるため、多くの論理列を含むクエリは遅くなります。

フィルター条件の先頭にワイルド カードを使用しない

先頭にワイルド カードが付いた条件 (明示的に、または ends-with のような演算子を使用して暗黙的に) を使用するクエリは、パフォーマンスが低下する可能性があります。 先頭にワイルド カードを使用したクエリで Dataverse はデータベース インデックスを利用できないため、SQL はテーブル全体をスキャンすることになります。 結果セットを制限する他の先頭にないワイルド カード クエリがある場合でも、テーブル スキャンが実行されることがあります。

次の例は、先頭にワイルド カードを使用する FetchXml 条件要素 です。

<condition attribute='accountnumber'
   operator='like'
   value='%234' />

次の例は、先頭にワイルド カードを使用する QueryExpression ConditionExpression です。

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 検索 を使用する。
  • 先頭のワイルド カードを必要性を避けるため、データ モデルを変更します。

文字列値の条件でワイルドカード文字を使用する方法の詳細

フィルター条件で数式や計算列を使用しない

式と計算済みの列 の値は、取得時にリアルタイムで計算されます。 これらの列にフィルターを使用するクエリは、フィルターを適用できるように、返される可能性のある各レコードの値を 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 種類の列があります。

これら両方の列の制限は、MaxLength プロパティを使用して指定されます。

850 文字未満に設定された MaxLength を持つ文字列に対して条件を使用できます。

850 文字を超える MaxLength を持つすべてのメモ列や文字列列は、大規模なテキスト列として Dataverse で定義されます。 大規模なテキスト列は、効果的にインデックスを作成するには大きすぎるため、フィルター条件に含めるとパフォーマンスが低下します。

Dataverse このような種類の列のデータをクエリするには、search の方が適しています。

クエリのヒント

重要

これらのオプションは、Microsoft テクニカル サポートによって推奨された場合にのみ適用してください。 これらのオプションを誤って使用すると、クエリのパフォーマンスが損なわれる可能性があります。

Microsoft SQL Server はクエリを最適化するための多くのクエリ ヒントをサポートします。 QueryExpression は、クエリヒントをサポートし、QueryExpression.QueryHints プロパティ を使用して、これらのクエリオプションを SQL Server に渡すことができます。

クエリ オプション SQL Server ヒント
OptimizeForUnknown 不明に最適化
ForceOrder 強制注文
DisableRowGoal ヒント: DISABLE_OPTIMIZER_ROWGOAL
EnableOptimizerHotfixes ヒント: ENABLE_QUERY_OPTIMIZER_HOTFIXES
LoopJoin ループ結合
MergeJoin マージ結合
HashJoin ハッシュ結合
NO_PERFORMANCE_SPOOL NO_PERFORMANCE_SPOOL
ENABLE_HIST_AMENDMENT_FOR_ASC_KEYS ヒント: ENABLE_HIST_AMENDMENT_FOR_ASC_KEYS

詳細: ヒント (Transact-SQL) - クエリ

ノー ロック

以前のバージョンでは、QueryExpression.NoLock プロパティ を使用してレコードの共有ロックを防止していました。 このプロパティを含める必要はなくなりました

ユニオンのヒント

異なるテーブルの列に ConditionExpression を設定する FilterExpression を追加する場合は、 FilterExpression.FilterHint プロパティunion に設定することで、パフォーマンスを向上させることができます。 ただし、いくつかの制限があります。

  • FilterExpression.FilterOperator は、LogicalOperator.Or を使用する必要があります。
  • 各クエリには union ヒントを 1 つだけ含めることができます。
  • FilterExpression ヒント付きの union フィルターが最上位フィルターにない場合は、Dataverse クエリを変換し、union ヒント付きのフィルターをルート フィルターに移動します。
  • union ヒントの深さが 3 レベルを超える場合、ヒントは無視されます。

以下の例では、アカウント テーブルと コンタクト テーブルの両方で、telephone1 列に union のヒントを持つフィルターを設定しています。

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 を使用して行を数える

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。