Data Model / DAXの基礎 2_ルールいろいろ①

Data ModelとDAXはPower Queryと違い、密接に関連するため、Data Model / DAXとして記載していくことにします。DAX*1に関する具体例に入る前に、まずはDAXを記述する上で留意すべき点(言い換えれば、基礎に関するベストプラクティス)について紹介をしたいと思います。

計算列 vs メジャー

DAXはPower BIの言語で、関数言語とクエリ言語の2つの側面を持ちます。DAXの特性についてポイントをまとめた記事(「DAXとは」)もご参考ください。 

DAXを使用する場合、メジャーと計算列という2つの概念があます。DAXでデータモデル(テーブル)に列を追加した場合、以下のようになります。

f:id:marshal115:20210423113343p:plain

DAXはPower BIでもExcelでも同じテクノロジーが使用されており、ExcelのPower Pivot(=Excel Data Model)で追加した場合は以下のようになります。

f:id:marshal115:20210423113742p:plain

どちらも列を追加することに変わりはなく、追加された列を「計算列」、英語ではCalculated Columnと言います。Excelのワークシートでデータセットの右に列を追加するイメージですが、この作業自体、実はデータモデルにPower Queryからデータをエ出力する前に行うことができれば、Power Query側で処理をしたほうが良いことが多いです。詳細な理由については別途話をしますが、出力されたデータのメモリ使用率の最適化が関係しています。

一方で、メジャーは”ポータブル関数”という特性があり、一度定義してしまえばどこでも使用できる特徴があります。数式は例えば以下のように記述します。

f:id:marshal115:20210423114720p:plain

Sales = SUM( Sales[SalesAmount] ) //Sales = Sales(売上高)
COGS = SUM( Sales[TotalCost] )  //COGS = Cost of Goods Sold(売上原価)
GM = [Sales] - [COGS]  //GM = Gross Margin(粗利益)
GM% = DIVIDE( [GM], [Sales] ) //GM% = Gross Margin %(粗利益率)

メジャーは計算列とは異なり、

クエリ時にその時のフィルター項目に左右されて結果が返される

仕様となっています。クエリ時とは、レポート上でビジュアル(テーブル、チャート等)を組み替えた際にDAXがそのビジュアルに従って計算結果を返すこと、となります。

例として、上記定義済の数式を以下のように、ProductCategoryNameで見た場合の結果とColorNameで見た場合の結果は、それぞれのフィルター項目によって数字が異なっているのが分かるかと思います(① vs ②)。ただし、この例ではあくまでExcelのPivotと同じように、フィルターされている項目が違うため結果数値も違うのであり、合計値で見れば同じ数字であることが確認できます。

f:id:marshal115:20210423122853p:plain

計算列とメジャーはどちらもDAXを使用して記述していきますが、詳細については別記事にて紹介をします。今回のメインは下記「列参照」と「メジャー参照」を行う際のベストプラクティスを紹介することが目的となります。

列参照 vs メジャー参照

上記のことを念頭に置いて、DAX式を参照する場合のベストプラクティスについて話をします。

ルールその①

  • メジャーを作るとき、必ずSales = SUM( テーブル名[列名] )とすること
    ポイントは、SUMの中は数値型の列名を参照していることであり、必ず
    テーブル名+ 列名 => Sales[SalesAmount]
    の両方を指定することです。
    これにより、これがメジャー列をベースに作成したベースメジャーであることが分かるようになります。ベースメジャーは私の中での用語であり、意味するところはこのメジャーをベースとして、他のメジャーをいろいろ作ることができる初期メジャー、と定義しています。

ルールその②

  • メジャーは他のメジャーから参照可能であり、その場合には以下のように[ ]だけで囲った形で記述します。例えば、上記GM(粗利益)の数式は以下の通りですが、メジャー同士を参照して新たなメジャーを作る場合は、Table名を含まない[ ]の中に既に構築済みのメジャー名が入るようにします。
    GM = [Sales] - [COGS] 
    この式は以下のように書き換えることができますが、一度定義したメジャーを再利用できることから、改めて下記のような書き方をしないことがベストプラクティスになります。
    GM = SUM( Sales[SalesAmount] ) - SUM( Sales[TotalCost] ) //この書き方をしなくても良い

 ルールその③

  • 計算列を作る際、参照する列は同じテーブル内の列を参照するときであっても「テーブル名 + 列名」、メジャーは[ ]だけを参照する
    繰り返しになりますが、計算列では以下のように記述します

    f:id:marshal115:20210423133439p:plain
    この例ではProductKey2という列をdCategoryテーブルに作る場合、まずはdCategoryとテーブルを指定し、その後同じテーブル内にある[ProductCategoryKey]列を指定して、10を乗じたものをProductKey2という計算列を追加しています。
    必ずテーブル名を追加する理由は、[ProductCategoryKey] * 10とした場合でも正しい結果が返ってきますが、ルール②のメジャーの記述方法と矛盾することになるため、紛らわしくなるためです(下図)。
    f:id:marshal115:20210423133927p:plain
    下記はメジャーだけを計算列に入れた場合であり、Power BI上では色のハイライトで判別可能ですが、それでも紛らわしいことが分かるかと思います。
    f:id:marshal115:20210423134337p:plain

  • DAXの中にDAX式をネストする場合は、メジャーと列参照を区別する
    上記分かりにくいパターンは例えば、下記のような式がある場合に分かりやすくなります。
    Large Sales = CALCULATE ( [Sales], Sales[SalesAmount] > 1000 )
    この例では、メジャーLarge Salesを定義するのですが、[Sales]というベースメジャーを用いて、Salesテーブルにある[SalesAmount]列から1000超となる項目だけを抽出する条件式でメジャーを作ることになります。
    見ての通り、もしSales[SalesAmount]が[SalesAmount]となっていた場合、参照先が列ではなく、[SalesAmount]というメジャーになっていると錯覚してしまいます。これが列参照 vs メジャー参照を区別するために最重要なポイントであり、必ず暗記すべきことになります。

DAX初心者のうち、このベストプラクティスを意識しないでDAXを記載することがありますが、DAX中級者以上になるとこのような記述方法は”初心者扱い”になってしまうため、面倒でも最初から心掛けることが重要です。

DAXのフォーマット

フォーマットされていないDAXDAXではない

これはDAX界のレジェンド的存在であるSQLBIのMarco RussoさんやAlberto Ferrariさんの言葉です。それもそのはず、以下のDAXはフォーマットしないと読解不能なコードとなってしまい、非常に効率が悪くなります。例えば、下記のようなDAX式がある場合、これが何を計算しているのか、見てすぐに理解できますか?

BrandInvTurn@Retail = CALCULATETABLE(ADDCOLUMNS(VALUES(dProduct[BrandName]),"@InvAmt", [InvAmt@Retail],"@InvTurn", [InvTurn]),ALLSELECTED())

恐らく、見た瞬間二度と目を向けることはなくなるでしょう。一方で、フォーマットしたバージョンは以下のようになります。

BottomN BrandInv@Retail =
CALCULATETABLE (
  ADDCOLUMNS (
    VALUES ( dProduct[BrandNameGBJ] ),
    "@InvAmt", [InvAmt@Retail],
    "@InvTurn", [InvTurn]
  ),
 ALLSELECTED ()
)

格段に見やすくなったのではないかと思います。初心者はこれでもまだ分かりにくいと言うかもしれませんが、少なくともフォーマットなし版と比べると、天と地の差になっているはずです。

実は私もこのフォーマットされた状態のDAXコードに慣れるまでに結構時間が掛かりましたが、これくらいのコードであれば、慣れると一瞬で何を算出しようとしているのかが分かるようになります。

上記未フォーマットのコードをフォーマットしてくれるサイトがSQLBIから提供されており、以下のURLにてコードをコピペし、FORMATボタンをクリックすれば、上記フォーマットされた状態に変換することができます。

www.daxformatter.com

ちなみに、SQLBIはData Model / DAXに関するスキルを学ぶ上で

この地球において最もクオリティの高いサイト

であり、沢山あるサイトの中、英語さえ問題なければSQLBI一択で良いほど、コンテンツの質と量が高いリソースとなります。

お勧めDAX学習サイト

www.sqlbi.com

それもそのはず、実はSQLBIのDAXレジェンドのお2人は

DAXの言語の開発に携わっている方々

なのです。Microsoftの社員ではないですが、Microsoftの社員が彼らのオンサイトトレーニングに参加するほど、DAX界ではレジェンドと言われている人たちです。SQLBIに関してはまた別途、記事にていろいろ紹介したいと思います。

まとめ

  • Data Model / DAXには計算列とメジャーの2つの概念がある
  • 計算列はデータモデルを構成するテーブルに列を追加すること
  • メジャーは”ポータブル関数”、すなわち定義式を記述することで、一度定義してしまえば、レポート内のあらゆるビジュアル(テーブル、チャート等)で使用可能
  • 列を参照するときは「テーブル名+列名」、メジャーを参照するときは[ ]の中にメジャー名を入れる
  • DAXは可読性を高めるため、フォーマットすることが非常に重要である
  • DAXに関する情報はSQLBIサイトにて全て取得可能

次回(ルールいろいろ②)はネーミングロジック(日本語 vs 英語表記)について話をしていきますが、DAXを記述する際の効率を決定するための基礎となります。  

marshal115.hatenablog.com

*1:Data Analysis eXpression, Power BIの言語