(Power BI) Mから始めよう #8 〜クエリ・パラメータの各種使い方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/aa045d83-0114-c06a-3cc0-5af3197812f0.png)
※この記事は、2021年10月21日に執筆しました。

 Power BI の Power Query エディターで使用するクエリ・パラメータの使い方について考えます。レポートで使用する What-ifパラメーターではありません。
　クエリ・パラメータは、クエリの中の値を別の場所に記述することで、メンテナンスがしやすくなります。例えば、エクセルファイルの保存場所とか、APIのキーなどをパラメータにして、PBIXファイルを他の人に配布したり、別の環境で使いたいときなど、Power Queryの中身を書き換えることなく使えるので便利です。ただし、PCの環境変数を参照するようなことはできません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/53d9663c-8653-a0eb-8bd3-0e61507c9ef1.png)

# A.クエリ・パラメータとテンプレート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/2a942bf3-2998-c4ac-350d-87842bda0e11.png)

## １．任意の値：データソースの参照先にパラメータを使う
※参照： [パラメーターを使用します。](https://docs.microsoft.com/ja-jp/power-query/power-query-query-parameters#using-parameters)

　通常、Webからのデータを取得するのには、以下のダイアログにURLを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/aa67c615-aeda-7f88-4fd4-ae33ed50a747.png)
　Power Query エディターの表示リボンにある「パラメータ」の「常に許可」にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/607ac5c5-0048-babe-a85f-f19eb1dac914.png)
　すると、以下のようにパラメータを参照するアイコンが表示され、パラメータの選択、新規パラメータ作成ができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/292db393-c651-58f1-7d00-33d694d40c90.png)
　この設定は、オプションと設定から行うこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/455f3fd9-db9e-ba6b-ee73-586179eeed9a.png)


## ２．値の一覧：パラメータをドロップダウンで選択する
※参照：[パラメーターのプロパティ](https://docs.microsoft.com/ja-jp/power-query/power-query-query-parameters#parameter-properties)

　パラメータの値を変更したいとき、候補の一覧から選択できると便利です。それを行うには、パラメータの新規作成で、「提案された値」を「値の一覧」にして、候補となる値をその下に張り付けます。（エクセルからペーストできます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/7b3f3471-9ec1-bf95-dc27-ca3fa39ec026.png)
　該当のクエリで、テキストフィルターのステップを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/a9af0de9-f723-3da3-2406-a1d1c592eed1.png)
　行のフィルターに、先ほど作ったパラメータを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/ce71a722-f323-0639-43dd-932a182e3a64.png)
　Power BIのメニューから「パラメーターの編集」で、先ほど登録した候補が選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/6d30f23d-ef7a-71f3-4470-cb762cce514d.png)
　選択を変更した後は、「変更の適用」ボタンが表示されますので、それを押すと、先ほどのパラメータが適用されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/7afc6333-347c-bf81-c2eb-04702794b8e5.png)

## ３．クエリ：候補を別で管理する
　パラメータ値の候補を別に持つこともできます。まず、データの入力でテーブルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/d5e194cb-4358-71ea-4c2d-fa917b56681b.png)
　次に「リストに変換」を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/af2ca69b-bcab-a826-7805-803304ba0b21.png)
　先ほどのパラメータの管理で、提案された値に「クエリ」を選択すれば、今作成したリストが選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/f5b77662-0d98-15e0-14fb-a7f40f3cea6b.png)
　以上で、先ほどと同じように、パラメータをドロップダウンで選択できるようになります。

### 候補を元のテーブルから作成する
　元のテーブルのCountryを右クリックし、「新しいクエリとして追加」を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/3c5e5a17-66e4-bca7-952a-5ee19f26e28e.png)
　Countryの項目がリストになって作成されますので、重複を削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/97aa691f-a649-ff16-8ec1-e3c02353f2b9.png)
　このリストを、先ほどと同様にパラメータの管理で。クエリに登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/b11b0dae-0827-b9ef-b964-e9d2813c0b38.png)
　元のテーブルのCountry項目のフィルターにこ登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/afd10910-89f2-a69f-4015-45aee919b79d.png)
　これで、パラメータのリストを手作りしなくて出来上がりました。

### テンプレート形式で保存すると、開始時にパラメータ選択ができます
※参照：[Deep Dive into Query Parameters and Power BI Templates](https://powerbi.microsoft.com/ja-jp/blog/deep-dive-into-query-parameters-and-power-bi-templates/)

 このファイルを、Power BIテンプレートファイル（拡張子 .pbit)で保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/7eee9b23-faf1-d6db-ac8e-1e243da456d5.png)
　このテンプレートファイルを開くと、最初にパラメータの選択ダイアログが表示されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/1459e905-914c-a801-35f5-a715589b3e11.png)


# B.カスタム関数とURLパラメータでWebスクレイピング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/7a30b3e6-3f03-2673-101e-3460afd91832.png)
※参照：[カスタム関数の引数](https://docs.microsoft.com/ja-jp/power-query/power-query-query-parameters#custom-function-argument)
※ビデオ：[Use Power BI Web From Example over multiple URLs](https://www.youtube.com/watch?v=s_5Jk8_fHjA&t=2s)

　連続したWebページのデータを取得したいとき、１ページづつ取得していくのもいいのですが、もっと簡単に取得する方法が欲しいです。テーブルの値をパラメータにして、関数に渡すことで、URLを書き換えて取得する方法を説明します。
　以前書いた記事も参照してください。※[Mから始めよう #6 〜Power Queryの関数を作成する](https://qiita.com/spumoni/items/6841aec922c02ee92a2f)

### １．注意事項；Webスクレイピングの注意点
　今回行う作業はWebスクレイピングという行為で、サイトによっては禁止されていることがあります。利用規約が書かれている場合は、確認してから利用しましょう。
　利用規約に書かれている中で、気を付けなければならない点の１つ目は、利用方法についてです。Webで一般公開されているデータを閲覧することに関しての制限はありませんが、利用方法については著作権法による制限が発生することがあります。公開に際しては、データの入手先についての記載を求めているものもあります。また、会員制限定で提供されている情報の場合、許可なく会員外に公開することはできません。その他、音声、映像を違法なサイトからダウンロードすると、処罰の対象となります。
　２点目は、アクセス方法です。Webスクレイピングなどの機械的なデータ取得が禁止されている場合があります。高負荷を与えてサービスに支障が出た場合、訴えられる可能性もあります。比較的新しいサーバーの場合、高負荷に耐えることができますが、過信してはいけません。一度に大量のアクセスが発生してDDoS攻撃とみなされないよう、１ページ取得ごとにインターバルを設けるというのが一般的です。
　robots.txtを読めという話題もありますが、あれは基本的にGooglebotなどの検索サイトが使っているロボット向けのファイルです。ロボットが広く巡回することは、クローリングと言ってますね。

### ２．カスタム関数の作成
　前置きは以上にして、本題に入ります。今回は、気象庁のホームページから気象情報をダウンロードします。利用規約は、以下の通りになっています。
　※[気象庁ホームページについて](https://www.jma.go.jp/jma/kishou/info/coment.html)

　今回使用する[「過去の気象データ検索」](https://www.data.jma.go.jp/obd/stats/etrn/index.php)は、1月ごとにデータが表示されています。１年のデータを取得するには、これを１２回行う必要があります。

**（※注）今回は、説明のために月ごとのデータを取得していますが、CSV形式でダウンロードも可能です。データが必要な場合は、ダウンロードサービスをご利用ください。
※[過去の気象データ・ダウンロード](https://www.data.jma.go.jp/gmd/risk/obsdl/index.php)**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/221dcfaa-772c-f3f4-ca33-4f0321c6606b.png)

　最初に、２０２０年１月のデータを取得し、データを整形して、クエリ名を「長野県上田市」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/d16ef667-6dae-25c3-00aa-6a4b24c2a515.png)

　このクエリを詳細エディタで開き、１行目に引数を指定し、URLの中身を書き換えます。

```:変更前
let
    ソース = Web.BrowserContents("https://www.data.jma.go.jp/obd/stats/etrn/view/daily_a1.php?prec_no=48&block_no=0402&year=2020&month=1&day=&view="),
```
```:変更後
(Year, Month) =>
let
    ソース = Web.BrowserContents(
                "https://www.data.jma.go.jp/obd/stats/etrn/view/daily_a1.php?prec_no=48&block_no=0402&" & 
                "year=" & Number.ToText(Year) & "&month=" & Number.ToText(Month) & "&day=&view="),
```

　日付の部分で、年月をリテラルで入力しているので、ここも変えてやります。

```:変更前
    追加されたプレフィックス = Table.TransformColumns(置換されたエラー, {{"日", each "2020/1/" & Text.From(_, "ja-JP"), type text}}),
```
```:変更後
    追加されたプレフィックス = 
        Table.TransformColumns(
            置換されたエラー, 
            {
                {"日", 
                 each Number.ToText(Year) & "/" & Number.ToText(Month) & "/" & Text.From(_, "ja-JP"),
                 type text
                }
            }
        ),
```

　これで、クエリは関数に代わり、以下のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/ba597755-4ec3-13e8-02e5-0fce2ccf5ace.png)

#### 年月テーブルの作成
　年、月の入ったてーぶろを作成します。最初は、からのクエリから2020年１月のみで作ります。

```
let
    source = Table.FromValue(2020, [DefaultColumnName = "Year"]),
    step1 = Table.AddColumn(source, "Month", each {1}),
    step2 = Table.ExpandListColumn(step1, "Month")
in
    step2
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/dec5117e-6423-9781-b9bd-64aea50f9d9f.png)

#### 列の追加で「カスタム関数の呼び出し」
　列の追加のリボンから、「カスタム関数の呼び出し」を選択し、関数クエリとパラメータを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/e22b0e19-fcc8-2c50-6c74-9f8dbd4b682f.png)
　以下のように、データがテーブルとして追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/895f2fde-1d56-42f1-14ff-a1ba2fe13320.png)
　これを、項目名の右の左右に開くボタンを押して、展開します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/f4bf545a-8d48-f9f1-7347-98a3cb0d55f1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/243c89b5-9225-5e80-49b1-50fe3fa9becb.png)
　以下のようなテーブルが出来上がります。YearとMonthの項目を削除して、年月日を日付、残りを10進数と、型を整えます。

### ３．遅延の設定
　今は、1ページのみの呼び出しでしたが、複数のページを読み込むためには、遅延を設定します。
　詳細エディタを開いて以下ように、「呼び出されたカスタム関数」のステップを変更します。遅延は1秒以上入れるのが望ましいので、今回は2秒にしておきました。

```
let
    source = Table.FromValue(2020, [DefaultColumnName = "Year"]),
    step1 = Table.AddColumn(source, "Month", each {1}),
    step2 = Table.ExpandListColumn(step1, "Month"),
    // 変更前
    //    呼び出されたカスタム関数 = Table.AddColumn(step2, "長野県上田市", each 長野県上田市([Year], [Month])),
    //
    // Function.InvokeAfterで、取得間隔を5秒に設定
    呼び出されたカスタム関数 = 
        Table.AddColumn(
            step2, 
            "長野県上田市", 
            each Function.InvokeAfter(()=> 長野県上田市([Year], [Month]), #duration(0,0,0,2))
        ),
    #"展開された 長野県上田市" = Table.ExpandTableColumn(呼び出されたカスタム関数, "長野県上田市", {"年月日", "降水量(mm)", "最高気温", "最低気温", "降雪(cm)"}, {"年月日", "降水量(mm)", "最高気温", "最低気温", "降雪(cm)"}),
    削除された列 = Table.RemoveColumns(#"展開された 長野県上田市",{"Year", "Month"}),
    変更された型 = Table.TransformColumnTypes(削除された列,{{"年月日", type date}, {"降水量(mm)", type number}, {"最高気温", type number}, {"最低気温", type number}, {"降雪(cm)", type number}})
in
    変更された型
```

### １年分のデータ取得
　クエリのステップ1を以下のように変更することで、1年分のデータが取得できます。

```
step1 = Table.AddColumn(source, "Month", each {1..12}),
```
 以上で2年分のデータが取得できますので、最後にPower BIのレポートでビジュアルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/1280dfa7-1278-e047-16d4-1ad8b80dda80.png)

# Ｃ．動的Ｍクエリ・パラメータ
※参照： [Power BI Desktop の動的 M クエリ パラメーター (プレビュー)](https://docs.microsoft.com/ja-jp/power-bi/connect-data/desktop-dynamic-m-query-parameters)
※ビデオ：[Power BI: Dynamic M query parameters](https://www.youtube.com/watch?v=hrPbf06aF9o)

　Power Queryの中で、パラメータを使用してデータを作成するのは、コンテンツの編集権限が必要です。しかし、動的クエリ・パラメータを使用すると、閲覧者がスライサーやフィルターを使用してPower Queryの動作を変えることができます。また、Power Queryでデータをフィルターするので、パフォーマンスの向上が期待できます。

　**ただし、一般にこの機能が必要なケースはあまりないと思われます。Azure Data Explore、Snowflake、Google BigQueryなどの一部のソースで役に立つと思われます。**

　動的クエリパラメータを使うには、**DirectQueryで接続している必要があります**。（サポートされていないDirectQuery接続もあります）加えて、下記のオプションの設定をしなければなりません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/f3999286-9a6a-c09b-7dce-96807664fa84.png)

　まず、Direct Queryでデータを取得し、パラメータを使った処理を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/57165b1f-3a50-eca5-9b35-cdc679eedab7.png)

　スライサーとするテーブルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/37fc6ea0-72d7-447f-9d9e-9d56f17adcab.png)

　Power Queryエディターを閉じるときに以下のような警告が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/8400fa0b-0539-d1ee-0324-834b4e44e32f.png)

 スライサーに設定したテーブルの項目を選択肢、プロパティの詳細を開くと、**「パラメーターにバインド」**という項目が表示されます。表示されない場合は、接続がDirect Queryになっているか、オプションのチェックが入っているか確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/0a695c3e-c1a9-2cee-0789-48552c7798c9.png)

　**「パラメータにバインド」**の項目に、パラメータの名前を選択すると、また警告が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/aad7e3f3-5201-9bd7-7767-8972bb05d6b6.png)

　レポートで、スライサーとテーブルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/96cf7df2-0a13-be64-e2cb-2339658d05ec.png)

　スライサーの選択を変更すると、テーブルが変更になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/58f5bea7-d7f6-b2e8-44f5-47eadadcfb67.png)

### 複数選択、すべて選択
　モデルビューで設定するプロパティで、「パラメーターにバインド」の下に**「複数選択」**と**「すべて選択」**のスイッチがあります。使用すると、パラメータはリスト形式で渡されますので、Power Queryの式を変更する必要があります。

```:変更前
let
    ・・・（省略）
    フィルターされた行 = Table.SelectRows(削除された他の列, each [crcd9_country] = Country)
in
    フィルターされた行
```
```:変更後
let
    ・・・（省略）
    フィルターされた行 = 
        if Type.Is(Value.Type(Country), List.Type) then
            if (List.Contains(Country, "__SelectAll__")) then    // すべて選択
                削除された他の列
            else                                                 // 複数選択
                Table.SelectRows(
                    削除された他の列, 
                    each List.Contains(Country, [crcd9_country])
                )
        else                                                     // 単一選択
            Table.SelectRows(
                削除された他の列, 
                each [crcd9_country] = Country
            )
in
    フィルターされた行
```

### 注意事項
※参照：Microsoft Docs [潜在的なセキュリティ リスク](https://docs.microsoft.com/ja-jp/power-bi/connect-data/desktop-dynamic-m-query-parameters#potential-security-risk)、[考慮事項と制限事項](https://docs.microsoft.com/ja-jp/power-bi/connect-data/desktop-dynamic-m-query-parameters#considerations-and-limitations)

- この機能は、現時点でプレビュー中です。Microsoft Docsの注意事項をよく読んでから試用してください。
- この機能を使用すると、インジェクション攻撃のリスクリが生じる可能性があります。
- この機能を使用すると、各種の制限が発生します。

# 関連記事

- [Mから始めよう #5 〜Power BIでWebコンテンツの利用と個人用ゲートウェイ〜](https://qiita.com/spumoni/items/f66bb43f0c3f13308d79)
- [[Power BI] Power Query の Web.Contents 関数でREST APIを使う](https://qiita.com/spumoni/items/419ffd1f07947a07cad0)
- [(Power BI) Mから始めよう #8 〜クエリ・パラメータの各種使い方](https://qiita.com/spumoni/items/4badf29d0b3e170fa15b#b%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E9%96%A2%E6%95%B0%E3%81%A8url%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%A7web%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0)
- [ページ分割されているWebでページ数がわからないデータを取得する](https://qiita.com/spumoni/items/2832b63fa627b20a5de2#4-%E3%83%9A%E3%83%BC%E3%82%B8%E5%88%86%E5%89%B2%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8Bweb%E3%81%A7%E3%83%9A%E3%83%BC%E3%82%B8%E6%95%B0%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)
- [(Power BI) Mから始めよう #10 エラー処理とHTTPステータスコード](https://qiita.com/spumoni/items/cd5843e0104fed9b4874)
- [[Power BI] Power QueryでCSSセレクタを使ってWebページからデータを取得する](https://qiita.com/spumoni/items/cad005c1deaa522531d8)