DAX 計算を最適化する

完了

パフォーマンス アナライザーが低速な DAX クエリを識別する場合、次の手順は DAX 自体を最適化することです。 非効率的な DAX は、セマンティック モデルのパフォーマンスが低下する最も一般的な原因の 1 つです。 メジャー定義を小さく変更すると、クエリ時間を大幅に短縮できます。

変数を使用して繰り返し計算を排除する

最も効果的な DAX 最適化手法の 1 つは、変数 (VAR / RETURN) の使用です。 数式が同じ式を複数回評価すると、エンジンによって毎回再計算されます。 変数は、式の結果を 1 回格納して再利用します。これを使用すると、部分式が繰り返される数式のクエリ時間をほぼ半分に短縮できます。

この前年比の成長尺度を考えてみましょう。

Sales YoY Growth % =
DIVIDE(
    ([Sales] - CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))),
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
)

PARALLELPERIOD式は、分子で 1 回、分母で 1 回評価されます。 変数を使用して書き換えることで、冗長性が排除されます。

Sales YoY Growth % =
VAR SalesPriorYear =
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
RETURN
    DIVIDE(([Sales] - SalesPriorYear), SalesPriorYear)

結果は同じですが、エンジンは 1 回だけ SalesPriorYear 評価します。 変数は読みやすさも向上します。わかりやすい変数名を使用すると、複雑な数式の理解と保守が容易になります。

ヒント

変数を使用すると、デバッグも簡略化されます。 RETURN式を一時的に変更して変数値のみを出力できます。これにより、数式全体を書き換えることなく中間結果を検査できます。

フィルターと KEEPFILTERS について

DAX でフィルターの変更を適用する方法は、パフォーマンスに直接影響します。 FILTER関数は、テーブルの行を 1 行ずつ反復処理して条件を評価します。 大きなテーブルに適用すると、このイテレーションはコストがかかる場合があります。

-- Expensive: FILTER iterates the entire Sales table
High Value Sales =
CALCULATE(
    [Total Sales],
    FILTER(Sales, Sales[Amount] > 1000)
)

数百万行のテーブルで FILTER を使用すると、エンジンによって行ごとの反復コンテキストが作成されます。 フィルター ロジックに含まれる列が 1 つだけの場合は、多くの場合、 FILTER を単純なブール式に置き換えることができます。

-- More efficient: column filter
High Value Sales =
CALCULATE(
    [Total Sales],
    Sales[Amount] > 1000
)

KEEPFILTERS は異なる目的を果たします。 置き換えではなく、既存のフィルター コンテキストが保持されます。 フィルター条件を追加する際に、スライサーやその他のビジュアルによって既に適用されている条件をオーバーライドしない場合は、KEEPFILTERSを使用します。

Online Sales =
CALCULATE(
    [Total Sales],
    KEEPFILTERS(Sales[Channel] = "Online")
)

FILTERと直接の列述語のパフォーマンスの違いは、大きなテーブルで最も顕著です。 一般的なルールとして、列レベルの述語で同様の結果が得られる場合は、テーブル全体にFILTERを適用しないようにしましょう。

反復子関数のコストを管理する

SUMXAVERAGEXMAXXCOUNTXなどの反復子関数は、テーブル内の各行の式を評価し、結果を集計します。 強力で必要な場合もありますが、特に大きなテーブルではコストがかかります。

-- Iterates every row in the Sales table
Weighted Average Price =
SUMX(
    Sales,
    Sales[Quantity] * Sales[UnitPrice]
) / SUM(Sales[Quantity])

この数式は正しく機能しますが、Sales テーブルに 5,000 万行がある場合、 SUMX はすべての行の乗算を評価します。 同等の非反復子式が存在する場合は、次の式を使用します。

-- Non-iterator equivalent (if LineTotal is a column)
Total Revenue = SUM(Sales[LineTotal])

反復子関数は本質的に悪いわけではありません。 単純な集計では表現できない行レベルの計算ロジックが必要な場合は、これらは適切な選択肢です。 重要なのはコストを理解することです。反復子のパフォーマンスはテーブル サイズでスケーリングされるため、1,000 行を超える反復子は問題ありませんが、1 億行を超える同じ反復子がボトルネックになる可能性があります。

コストのかかるパターンを回避する

特定の DAX パターンは、パフォーマンスの問題を引き起こすことがわかっています。 これらを認識すると、最初からより良い数式を記述するのに役立ちます。

大規模なテーブルに対する COUNTROWS(FILTER(...))。 このパターンでは、テーブル全体を反復処理して、条件に一致する行をカウントします。 これを CALCULATE に置き換え、 COUNTROWS をフィルター引数に置き換えます。

-- Expensive
Large Orders = COUNTROWS(FILTER(Sales, Sales[Amount] > 1000))

-- Better
Large Orders = CALCULATE(COUNTROWS(Sales), Sales[Amount] > 1000)

複雑なフィルターを含む入れ子になった CALCULATE。 入れ子になった各 CALCULATE は、新しいフィルター コンテキスト遷移を作成します。 複数のコンテキストが変更された深く入れ子になった数式は、最適化が困難です。 複雑なメジャーを、それぞれ 1 つのフィルター変更を処理する小さなコンポーネント メジャーに分割して簡略化します。

集計グレインを混在させます。 異なるレベルの粒度でデータを結合するメジャー (たとえば、1 つの行の値をテーブル レベルの合計と比較する) には、コストがかかる可能性のあるコンテキスト遷移が必要です。 変数を使用して合計を 1 回評価し、再利用します。

Pct of Total =
VAR TotalSales = CALCULATE([Total Sales], REMOVEFILTERS())
RETURN
    DIVIDE([Total Sales], TotalSales)

計算をデータ レイヤーに移動する

DAX メジャーがすべてのクエリ更新で同じ結果を計算し、基になるデータが更新間で変更されない場合は、代わりにデータ レイヤーでその計算を具体化することを検討してください。

たとえば、姓と名を連結する計算列は、データ更新中に実行され、結果が格納されます。 測定値と同じロジックは、すべてのクエリで実行されます。 このような静的変換には、次の 2 つのオプションがあります。

  • Power Query計算列: データの読み込み中に M で変換を定義します。 VertiPaq エンジンは読み込み中にストレージを最適化できるため、これらの列は DAX 計算列よりも効率的に圧縮されます。
  • ソース レベルの計算: データ ソースが SQL データベースの場合は、SQL ビューまたはクエリに計算を追加します。 これにより、データベース エンジンの最適化機能が使用されます。

メジャーの評価やセマンティック モデルのリレーションシップに依存するタイム インテリジェンス関数の使用など、DAX 固有の関数を必要とするシナリオ用に DAX 計算列を予約します。

重要

計算をアップストリームに移動すると、クエリのパフォーマンスが向上するだけでなく、すべてのPower Query テーブルの読み込みが完了した後に DAX 計算列が評価されるため、データ更新時間も短縮されます。

AI エクスペリエンスを検討する

DAX のパフォーマンスは、AI を利用したエクスペリエンスに直接影響します。 Microsoft Fabricでは、IQ データ エージェントとCopilot チャットは、バックグラウンドで DAX クエリを生成してセマンティック モデルにクエリを実行します。 人間のユーザーが完了するのに5秒かかる操作は、Copilotでも同様に5秒かかります。そして、AIのインタラクションでは、多くの場合、対話型レポートよりもタイムアウトのしきい値が厳密です。

DAX の最適化は、レポートの高速化だけではありません。 これは、自然言語クエリ、自動化されたエージェント、リアルタイム分析をサポートするのに十分な応答性をデータに提供することです。