SUMMARIZECOLUMNS関数の研究～SUMMARIZE関数は非推奨?🤔～

# はじめに

すこし複雑なメジャーを記述しようとすると、避けては通れないテーブル操作関数。主要な`SUMMARIZE`、`ADDCOLUMNS`、 `SUMMARIZECOLUMNS`などを研究してみます。

**テーブル操作関数シリーズ**

続きものです。

1. [SUMMARIZE関数の研究～集計列の追加はなぜ非推奨なのか🤔～](https://qiita.com/akihiro_suto/items/a317d560fd298403bc15)
1. [ADDCOLUMNS関数の研究～集計列追加のベストプラクティス🤔～](https://qiita.com/akihiro_suto/items/ced4c9053f11fbe2baf3)
1. [集計列追加のベストプラクティス🤔～とはいえ例外だってあるぞ～](https://qiita.com/akihiro_suto/items/331aee0734b98c0ec345)
1. [SUMMARIZECOLUMNS関数の研究～SUMMARIZE関数は非推奨?🤔～](https://qiita.com/akihiro_suto/items/1a7c08aaf369ee1cc3a3)
1. [GROUPBY関数の研究🤔～基本動作確認編～](https://qiita.com/akihiro_suto/items/aaf2e4c98cf88833dc63)

## DAXクエリ＆DAX.do

https://dax.do/

今回の検証はDAX.do上でDAXクエリを書いて検証していきます。
DAXクエリがわからない方は先にこの記事を読んで、DAX.doで少し触ってからのほうが理解が進むでしょう。

DAXクエリについてはこちらの記事を参照してください。

https://qiita.com/hanaseleb/items/96b80210a7fc20c36a13

# 参考記事

https://www.sqlbi.com/articles/introducing-summarizecolumns/

# `SUMMARIZECOLUMNS`関数の文法

https://dax.guide/summarizecolumns/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/25595822-37df-317c-2d64-a2c1f388776d.png)

# まず書いてみる

`SUMMARIZE`関数や`ADDCOLUMNS`関数と違って、`SUMMARIZECOLUMNS`関数は元のテーブルを必要としないため、直感的に記述していくことができます。

```
EVALUATE
SUMMARIZECOLUMNS ( 'Product'[Category], Customer[Country])
ORDER BY
'Product'[Category]
```

結果

列のグループ化は簡単。**リレーションシップが直接つながっていないテーブル同士**でも問題なく動作している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/04d7f032-84d4-a7f5-639a-8b91534c78de.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/b60c063a-f526-87c9-afde-ab922750f529.png)

## `CROSSJOIN` 関数でも同じことがかける

```CROSSJOIN.dax
EVALUATE
CROSSJOIN ( VALUES ( 'Product'[Category] ), VALUES ( Customer[Country] ) )
ORDER BY 'Product'[Category]
```

## もちろん`SUMMARIZE`関数でも同じことが書ける

リレーションがつながっていないため、`SUMMARIZE`関数で最初に指定するテーブルに`Product`テーブルや`Customer`テーブルを指定しても、エラーが返ってくる。

```エラーになる.dax
EVALUATE
SUMMARIZE ( 'Product', 'Product'[Category], Customer[Country] )
ORDER BY 'Product'[Category]
```

```エラーになる.dax
EVALUATE
SUMMARIZE ( 'Customer', 'Product'[Category], Customer[Country] )
ORDER BY 'Product'[Category]
```

列が見つからないよとエラーが出る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/2b15cb05-9cc9-31ea-e344-70eb0ea504c2.png)

### 正しい記述方法

```正しい記述方法Salesテーブルを指定する.dax
EVALUATE
SUMMARIZE ( 'Sales', 'Product'[Category], Customer[Country] )
ORDER BY 'Product'[Category]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/7532f840-ec8d-ffab-a219-7fbd7b73b6ff.png)

結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/955f3756-5297-6090-852a-6a9d8ae9a3b6.png)

`SUMMARIZE`関数でも同じ結果を出すことができました。

## 集計列の追加をしてみる。

### `SUMMARIZECOLUMNS`関数

```集計列の追加.dax
EVALUATE
SUMMARIZECOLUMNS (
    'Product'[Category],
    Customer[Country],
    "SalesAMT",
    SUMX ( 
        'Sales', Sales[Net Price] * Sales[Quantity] 
    )
)
ORDER BY 'Product'[Category]
```

正しい数値がでています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/096555bf-0dfc-d1d4-ac46-925c43a3b16d.png)

#### フィルターコンテキストを変更してみる。

```フィルターコンテキストを変更する.dax
EVALUATE
SUMMARIZECOLUMNS (
    'Product'[Category],
    Customer[Country],
    "SalesAMT", SUMX ( 'Sales', Sales[Net Price] * Sales[Quantity] ),
    "TotalAMT",
        CALCULATE (
            SUMX ( 'Sales', Sales[Net Price] * Sales[Quantity] ),
            REMOVEFILTERS ( 'Product'[Category] ),
            REMOVEFILTERS ( Customer[Country] )
        )
)
ORDER BY [SalesAMT] DESC
```

`SUMMARIZE`関数だけでは難しかったフィルターコンテキストの操作も直感的に行うことができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/c2be40e8-a601-7818-e8e8-328c341b44b2.png)

# パフォーマンス比較 （`SUMMARIZE`関数と`SUMMARIZECOLUMNS`関数）

同じ結果がでるクエリを書いて実行してみる。

```SUMMARIZE/ADDCOLUMNS.dax
EVALUATE
ADDCOLUMNS (
    SUMMARIZE (
        Sales,
        'Product'[Brand],
        Customer[Country]
    ),
    "SalesAMT",
        CALCULATE (
            SUMX (
                Sales,
                Sales[Net Price] * Sales[Quantity]
            )
        )
)
ORDER BY [SalesAMT] DESC
```

```SUMMARIZECOLUMNS.dax
EVALUATE
SUMMARIZECOLUMNS (
    'Product'[Brand],
    Customer[Country],
    "SalesAMT",
        SUMX (
            Sales,
            Sales[Net Price] * Sales[Quantity]
        )
)
ORDER BY [SalesAMT] DESC
```

結果は同じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/94ebc59e-4930-99e3-d7c7-996baafa3003.png)

DAX Studioで結果を確認。発行されているクエリの数が`SUMMARIZECOLUMNS`関数では１つだけ。`SUMMARIZE`関数で作成したDAXクエリの方は２回スキャンが走ってしまっており、行数が増えてきたときにパフォーマンスの差がでそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/7a23cee2-85d8-80e5-6960-be0aaa46467b.png)

# SUMMARIZE関数は非推奨？🤔

[参考記事](https://www.sqlbi.com/articles/introducing-summarizecolumns/)から引用します。

`SUMMARIZE`関数はほとんど非推奨と考えた方がよいでしょう。`SUMMARIZE`の唯一の安全な使用法は、追加式を含まず、テーブルを結合してSQL言語の`SELECT DISTINCT`と同等の結果を得るためにのみ`SUMMARIZE`を使用する場合です。

`SUMMARIZECOLUMNS`関数は、非常に最適化されたクエリプランを生成します。そして、`SUMMARIZE`や`ADDCOLUMNS/SUMMARIZE`パターンの代わりとして考慮すべきです。

> Conclusion
>You should consider the SUMMARIZE function almost like a deprecated one. The only safe use of SUMMARIZE is when you do not include additional expressions, and you use SUMMARIZE only to join tables and obtain a result equivalent to a SELECT DISTINCT in SQL language.
>The SUMMARIZECOLUMNS function produces a very optimized query plan, and you should consider it as a replacement to SUMMARIZE and ADDCOLUMNS/SUMMARIZE pattern.

# 注意点

便利で処理も早い`SUMMARIZECOLUMNS`関数ですが、比較的最近追加された関数のため、使用できるモデルに制限があります。

- Excel 2010: サポートされていません。
- Excel 2013: サポートされていません。
- Excel 2016: 外部フィルターコンテキストがない場合のみサポートされます (*)
- Analysis Services 2012: サポートされていません。
- Analysis Services 2014: サポートされていません。
- Analysis Services 2016: サポートされています。
- Power BI Desktop: サポートされています。

(*) Excel 2016 では、SUMMARIZECOLUMNS に外部フィルター コンテキスト（CALCULATE や CALCULATETABLE など）を適用できないため、メジャー式で使用することはできません。

# まとめ

まだ理解しているとは言い難いけど、手を動かして調べていくと少しわかってくるもんだ。