Power Queryを画面で操作して作ったクエリは、内部ではM言語というコードとして記録されています。詳細エディターを開くと、このコードを直接読んだり書き換えたりできます。記号が多くて最初は読みにくく感じますが、「let と in」「ステップ名」「関数呼び出し」の3つに分けて見ると読めるようになります。
この記事では、この3つの読み方と、既存のコードを壊さずに少しだけ直す手順を説明します。個々の関数の詳しい使い方は扱いません。
編集する前に:元に戻せるようにしておく
詳細エディターでコードを直すと、画面で操作するよりクエリが壊れやすくなります。触る前に、次のどちらかをしておきます。
- クエリを複製する:クエリの一覧で対象のクエリを右クリックし、「複製」を選びます。試すのは複製したほうです。
- コードを丸ごと保存しておく:詳細エディターの中身をすべて選択してコピーし、メモ帳などに貼り付けて保存します。
うまくいかなかったときは、保存しておいたコードを貼り戻せば元に戻ります。
詳細エディターの開き方
Power Queryエディターを開き、「ホーム」タブ(または「表示」タブ)の「詳細エディター」をクリックします。Excelのバージョンによってボタンの位置や名前が違うことがあります。見当たらないときは、リボンの各タブを確認してください。
全体の形:let と in
たとえば、CSVを取り込んで1行目を見出しにし、型を変えてから金額が空の行を除くクエリは、次のようなコードになります。
let
ソース = Csv.Document(File.Contents("C:\Data\sales.csv"), [Delimiter=",", Encoding=65001]),
#"昇格されたヘッダー数" = Table.PromoteHeaders(ソース, [PromoteAllScalars=true]),
#"変更された型" = Table.TransformColumnTypes(#"昇格されたヘッダー数", {{"日付", type date}, {"金額", Int64.Type}}),
#"フィルターされた行" = Table.SelectRows(#"変更された型", each [金額] <> null)
in
#"フィルターされた行"
コード全体のルールは次の4つです。
letとinの間に、「名前 = 式」の行を並べる- 各行の最後にはカンマを付ける。ただし、
inの直前の行には付けない inの後ろには、クエリの結果として返す名前を1つ書く- 「名前 = 式」の1つが、画面右側の「適用したステップ」の1行に対応する
1行が長くて折り返していても、次のカンマまでが1つのステップです。
ステップ名は「変数」として読む
ソース や #"変更された型" などのステップ名は、その時点のテーブルを入れておく変数です。次のステップは、関数の引数に前のステップの名前を書くことで、そのテーブルを受け取ります。
上の例をステップごとに分けると、次のようになります。
| ステップ名 | していること | 受け取っているステップ |
|---|---|---|
| ソース | CSVファイルを読み込む | (なし) |
| #"昇格されたヘッダー数" | 1行目を見出しにする | ソース |
| #"変更された型" | 日付と金額の型を設定する | #"昇格されたヘッダー数" |
| #"フィルターされた行" | 金額が空の行を除く | #"変更された型" |
読むときのポイントは次のとおりです。
- どのステップを受け取るかは名前で決まる:各ステップが受け取るのは、すぐ上の行ではなく、引数に書かれた名前のステップです。コードを読むときは、関数の引数(多くは1つ目)に書かれた名前をたどります。
- #"..." で囲む名前:スペースや記号を含む名前は
#"..."で囲みます。日本語の名前は、自動で作られたコードで囲まれている場合も、囲まれていない場合もあります。自分で名前を付けるときは、#"..."で囲むか、英数字だけの名前にすると迷いません。 - 名前の変更は「適用したステップ」で行う:画面右側の「適用したステップ」で名前を変えると、通常はその名前を参照している箇所も合わせて変わります。詳細エディターで直接書き換える場合は、その名前を使っている箇所をすべて自分で直す必要があります。
関数呼び出しの読み方
各ステップの「=」の右側は、多くが 関数名(引数1, 引数2, ...) という形の関数呼び出しです。
Table.SelectRows(#"変更された型", each [金額] <> null)
Table.SelectRows:関数名です。「Table」が対象の種類、「SelectRows」が動作を表します。Text.、Number.、Date.、List.などで始まる関数も、同じように読めます。- 1つ目の引数
#"変更された型":処理するテーブルです。前のステップの名前がよく入ります。 - 2つ目の引数
each [金額] <> null:どの行を残すかの条件です。
値の書き方
引数には、次のような形の値が出てきます。
| 書き方 | 意味 | 例 |
|---|---|---|
| "..." | 文字列 | "日付" |
| 数字 | 数値 | 1000、65001 |
| { } | リスト(値を並べたもの) | {"日付", "金額"} |
| {{ }, { }} | リストを並べたリスト | {{"日付", type date}, {"金額", Int64.Type}} |
| [名前=値] | レコード(名前の付いた値の組) | [Delimiter=","] |
| [列名] | each の中では、その行の列の値 | [金額] |
| null | 値がないこと | null |
| type ~ / ~.Type | 型 | type text、type date、Int64.Type |
[ ] の中に [Delimiter=","] のような「名前=値」があればレコードです。each の中で [金額] のように列名だけが入っていれば、その行の列の値を指します。
each は「1行ずつ実行する短い関数」
each [金額] <> null は、「行ごとに、金額が null でなければ true を返す関数」を短く書いたものです。省略しないで書くと (行) => 行[金額] <> null となり、=> の左が受け取る値、右が返す式です。each の中の [列名] は、いま処理している行の値を指します。
大文字と小文字は区別される
M言語では大文字と小文字が区別されます。Table.SelectRows を table.selectrows と書くとエラーになります。列名も同じで、[金額] の表記が実際の列名と1文字でも違うと、列が見つかりません。
関数の引数を調べる方法
関数の引数の順番や型が分からないときは、Power Queryエディターで空のクエリを作り、数式バーに = Table.SelectRows のように関数名だけをかっこなしで入力します。その関数の説明と引数の一覧が表示されます。さらに詳しく知りたいときは、Microsoftの公式ドキュメント(Power Query Mの関数リファレンス)で関数名を検索してください。
小さな修正1:途中にステップを1つ追加する
例として、型を変えた直後に「備考」列を削除するステップを追加します。
修正前(該当する部分)
#"変更された型" = Table.TransformColumnTypes(#"昇格されたヘッダー数", {{"日付", type date}, {"金額", Int64.Type}}),
#"フィルターされた行" = Table.SelectRows(#"変更された型", each [金額] <> null)
修正後
#"変更された型" = Table.TransformColumnTypes(#"昇格されたヘッダー数", {{"日付", type date}, {"金額", Int64.Type}}),
#"備考を削除" = Table.RemoveColumns(#"変更された型", {"備考"}),
#"フィルターされた行" = Table.SelectRows(#"備考を削除", each [金額] <> null)
直したのは次の2か所です。
- 新しい行を追加し、
#"変更された型"を受け取るようにした(行の最後にカンマを付ける) - 次の行の
#"フィルターされた行"が受け取るステップを、#"変更された型"から#"備考を削除"に変えた
2を忘れると、新しいステップはどこからも参照されず、結果に反映されません。エラーにもならないので気付きにくい失敗です。
小さな修正2:参照先を変えて、ステップを飛ばす
あるステップを一時的に外した結果を見たいときは、参照先を1つ前のステップに変えます。
たとえば、金額が空の行を除く前の状態を最終結果にしたい場合は、in の後ろを次のように変えます。
in
#"変更された型"
こうすると、#"フィルターされた行" は結果に使われなくなります。確認が終わったら元に戻すか、不要ならそのステップの行ごと削除します。使われないステップを残しておくと、あとで読んだ人が「このステップは結果に反映されている」と思い込む原因になります。
小さな修正3:何度も使う値を変数にまとめる
条件の値がコードのあちこちに直接書かれていると、変更するときに直し漏れが起きます。let の最初に変数として1回だけ書き、そこを参照するようにします。
let
最低金額 = 1000,
ソース = Csv.Document(File.Contents("C:\Data\sales.csv"), [Delimiter=",", Encoding=65001]),
#"昇格されたヘッダー数" = Table.PromoteHeaders(ソース, [PromoteAllScalars=true]),
#"変更された型" = Table.TransformColumnTypes(#"昇格されたヘッダー数", {{"日付", type date}, {"金額", Int64.Type}}),
#"フィルターされた行" = Table.SelectRows(#"変更された型", each [金額] <> null and [金額] >= 最低金額)
in
#"フィルターされた行"
最低金額 = 1000, も、1つのステップとして「適用したステップ」に表示されます。値を変えたいときは、この1行だけを直せば済みます。
エラーが出たときの確認の順番
詳細エディターで「完了」を押したときやプレビューを表示したときにエラーが出たら、次の順番で確認します。エラーメッセージの文言はバージョンや言語設定によって違うため、メッセージに含まれる言葉を手がかりにします。
| 症状 | 最初に見る場所 |
|---|---|
| 構文エラーでエディターを閉じられない(カンマやトークンに関するメッセージ) | 各行の最後のカンマの有無、in の直前の行に余分なカンマがないか、かっこや引用符の閉じ忘れ |
| 名前が認識されないというエラー | ステップ名と関数名のつづり、#"..." の付け忘れ、大文字と小文字 |
| 列が見つからないというエラー | 列名のつづり、前のステップで列名が変わっていないか |
| エディターは閉じられるが、プレビューのセルがErrorになる | 型の変換。Errorのセルをクリックすると、下に詳しい内容が表示される |
どこでエラーが起きているか分からないときは、「適用したステップ」を上から順にクリックし、どのステップからプレビューがおかしくなるかを確かめます。
修正したあとに確かめること
- 「適用したステップ」を上から順にクリックし、各ステップのプレビューが想定どおりか
- 最後のステップの列と行が、修正前と比べて想定どおりに変わっているか(エディターのプレビューで見られる行数には上限があるため、件数は読み込み先で確認する)
- 「閉じて読み込む」を実行したあと、読み込み先の表の件数が想定と合っているか
- 元データを変えてから「更新」しても、同じ手順で結果が作られるか
画面の操作で作ったステップと、手で直したステップが同じクエリに混ざっていても問題ありません。一度に大きく書き換えるより、1か所直しては確認する作業を繰り返すほうが、壊さずに済みます。
