この記事はMicrosoft Power BI Advent Calendar 2025に参加しています。
使用しているPower BIは2025/12/21時点の最新バージョンです。
Power PlatformやDynamics365を触ったことのある方はMicrosoft Dataverseも同時に触っていると思います。
今回の記事ではMicrosoft DataverseをPower BIで可視化する際のハードルの一つである選択肢列(Picklist, MultiSelectPicklist)をPower Queryで読み出す方法について書いていきます。
選択肢列について
選択肢列の作成
選択肢列は定義した選択肢のリストに基づく一つ、または複数の値を選択して入力することができます。
これらの選択肢列は環境全体で共有するグローバル選択肢と、個別のテーブルに属するローカル選択肢のどちらかを設定できます。
ここから先は一つの値を選択する選択肢列を単一選択肢列、複数の値を選択する選択肢列を複数選択肢列と呼称します。
また、グローバルとローカルを区別する必要がある場合はグローバル単一選択肢列のように呼称します。
選択肢列に定義された選択肢のグループはPicklistと呼称します。
選択肢列の詳しい説明はMicrosoft Learnの選択肢の作成をご覧ください。

Dataverseで入力済みの選択肢列が含まれたテーブルがどう表示されるか
Dataverseでは単一選択肢列なら選んだラベルが一つ、複数選択肢列なら選んだラベルが,区切りで表示されます。
この表示はグローバルな列とローカルな列でも差はありません。

Power Queryで入力済みの選択肢列が含まれたテーブルがどう表示されるか
同じテーブルをPower Queryで読み込んでみました。

Power Queryではラベルは表示されず、値が単一選択肢列なら一つ、複数選択肢列なら,区切りで表示されます。
つまり、選択肢列で定義されたPicklistを別途Power Queryで読み出さない限り、本来表示したいラベルの入力内容をPower BI上で復元することは出来ません。
Power QueryでPicklistを読み出す
Picklistを読み出す方法
Picklistを読み出すにはWeb.Contents関数とDataverseのWebAPIを使用します。
方法としては三つあります。
- GlobalOptionSetDefinitionsからグローバル変数を見つける
- 特定のテーブルに使われているPicklistを取得する
- 特定のテーブルに使われているMultiSelectPicklistを取得する
グローバルとローカル、単一選択肢列と複数選択肢列で使える読み出し方法が異なります。
表にすると以下のようになります。
| 列の種類/定義する場所 | グローバル | ローカル |
|---|---|---|
| 単一選択肢列 | 1, 2 | 2 |
| 複数選択肢列 | 1, 3 | 3 |
グローバル変数はGlobalOptionSetDefinitionsから単一選択肢列と複数選択肢列をまとめて取得できます。
しかしローカル変数はWebAPIからテーブルを直接指定することでしか取得できず、しかも単一選択肢列と複数選択肢列で取得する方法が異なります。
細かい理屈は専門外なので実際の方法については式と結果だけを書いていきます。
GlobalOptionSetDefinitionsからグローバル変数を見つける
式は以下の通りです。
let
ソース = Json.Document(Web.Contents("https://" & [DataverseのURL] & "/api/data/v9.0/GlobalOptionSetDefinitions")),
テーブルに変換済み = Table.FromRecords({ソース}),
#"展開された value" = Table.ExpandListColumn(テーブルに変換済み, "value"),
#"展開された value1" = Table.ExpandRecordColumn(#"展開された value", "value", {"@odata.type", "ParentOptionSetName", "IsCustomOptionSet", "IsGlobal", "IsManaged", "Name", "ExternalTypeName", "OptionSetType", "IntroducedVersion", "MetadataId", "HasChanged", "Options", "TrueOption", "FalseOption", "Description", "DisplayName", "IsCustomizable"}, {"value.@odata.type", "value.ParentOptionSetName", "value.IsCustomOptionSet", "value.IsGlobal", "value.IsManaged", "value.Name", "value.ExternalTypeName", "value.OptionSetType", "value.IntroducedVersion", "value.MetadataId", "value.HasChanged", "value.Options", "value.TrueOption", "value.FalseOption", "value.Description", "value.DisplayName", "value.IsCustomizable"}),
フィルターされた行 = Table.SelectRows(#"展開された value1", each ([value.Name] = [選択肢列の名前])),
#"value Options" = フィルターされた行{0}[value.Options],
テーブルに変換済み1 = Table.FromList(#"value Options", Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"展開された Column1" = Table.ExpandRecordColumn(テーブルに変換済み1, "Column1", {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}, {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}),
削除された他の列 = Table.SelectColumns(#"展開された Column1",{"Value", "Color", "IsHidden", "Label"}),
#"展開された Label" = Table.ExpandRecordColumn(削除された他の列, "Label", {"UserLocalizedLabel"}, {"UserLocalizedLabel"}),
#"展開された UserLocalizedLabel" = Table.ExpandRecordColumn(#"展開された Label", "UserLocalizedLabel", {"Label", "LanguageCode"}, {"Label", "LanguageCode"}),
変更された型 = Table.TransformColumnTypes(#"展開された UserLocalizedLabel",{{"Value", Int64.Type}, {"Color", type text}, {"IsHidden", type logical}, {"Label", type text}, {"LanguageCode", Int64.Type}})
in
変更された型
[DataverseのURL]と[選択肢列の名前]をそれぞれ置き換えれば読み出せます。
式の結果はこのようになります。

Valueをキーにマージするなりリレーションシップを組めば選択肢列のラベルを表示することができます。
特定のテーブルに使われているPicklistを取得する
式は以下の通りです。
let
ソース = Json.Document(Web.Contents("https://" & [DataverseのURL] , [RelativePath="/api/data/v9.1/EntityDefinitions(LogicalName='[ローカル選択肢列が格納されているテーブル名]')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$select=LogicalName&$expand=OptionSet($select=Options)"])),
value = ソース[value],
テーブルに変換済み = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"展開された Column1" = Table.ExpandRecordColumn(テーブルに変換済み, "Column1", {"LogicalName", "MetadataId", "OptionSet"}, {"LogicalName", "MetadataId", "OptionSet"}),
フィルターされた行 = Table.SelectRows(#"展開された Column1", each ([LogicalName] = "[読み出したい単一選択肢列名]")),
OptionSet = フィルターされた行{0}[OptionSet],
Options = OptionSet[Options],
テーブルに変換済み1 = Table.FromList(Options, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"展開された Column2" = Table.ExpandRecordColumn(テーブルに変換済み1, "Column1", {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}, {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}),
削除された他の列 = Table.SelectColumns(#"展開された Column2",{"Value", "Color", "Label", "IsHidden"}),
#"展開された Label" = Table.ExpandRecordColumn(削除された他の列, "Label", {"UserLocalizedLabel"}, {"UserLocalizedLabel"}),
#"展開された UserLocalizedLabel" = Table.ExpandRecordColumn(#"展開された Label", "UserLocalizedLabel", {"Label", "LanguageCode", "IsManaged", "HasChanged"}, {"Label", "LanguageCode", "IsManaged", "HasChanged"}),
変更された型 = Table.TransformColumnTypes(#"展開された UserLocalizedLabel",{{"Value", Int64.Type}, {"LanguageCode", Int64.Type}, {"Color", type text}, {"Label", type text}, {"IsManaged", type logical}, {"IsHidden", type logical}})
in
変更された型
[DataverseのURL]と[ローカル選択肢列が格納されているテーブル名]をそれぞれ置き換えれば、展開された Column1ステップでそのテーブルに使われているグローバルとローカルの単一選択肢列が表示されます。

読み出したい単一選択肢列だけになるようにフィルターをかけます。

特定のテーブルに使われているMultiSelectPicklistを取得する
式は以下の通りです。
let
ソース = Json.Document(Web.Contents("https://" & [DataverseのURL] , [RelativePath="/api/data/v9.1/EntityDefinitions(LogicalName='[ローカル選択肢列が格納されているテーブル名]')/Attributes/Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata?$select=LogicalName&$expand=OptionSet($select=Options)"])),
value = ソース[value],
テーブルに変換済み = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"展開された Column1" = Table.ExpandRecordColumn(テーブルに変換済み, "Column1", {"LogicalName", "MetadataId", "OptionSet"}, {"LogicalName", "MetadataId", "OptionSet"}),
フィルターされた行 = Table.SelectRows(#"展開された Column1", each ([LogicalName] = "[読み出したい複数選択肢列名]")),
OptionSet = フィルターされた行{0}[OptionSet],
Options = OptionSet[Options],
テーブルに変換済み1 = Table.FromList(Options, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"展開された Column2" = Table.ExpandRecordColumn(テーブルに変換済み1, "Column1", {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}, {"Value", "Color", "IsManaged", "ExternalValue", "ParentValues", "Tag", "IsHidden", "MetadataId", "HasChanged", "Label", "Description"}),
削除された他の列 = Table.SelectColumns(#"展開された Column2",{"Value", "Color", "Label", "IsHidden"}),
#"展開された Label" = Table.ExpandRecordColumn(削除された他の列, "Label", {"UserLocalizedLabel"}, {"UserLocalizedLabel"}),
#"展開された UserLocalizedLabel" = Table.ExpandRecordColumn(#"展開された Label", "UserLocalizedLabel", {"Label", "LanguageCode", "IsManaged", "HasChanged"}, {"Label", "LanguageCode", "IsManaged", "HasChanged"}),
変更された型 = Table.TransformColumnTypes(#"展開された UserLocalizedLabel",{{"Value", Int64.Type}, {"LanguageCode", Int64.Type}, {"Color", type text}, {"Label", type text}, {"IsManaged", type logical}, {"IsHidden", type logical}})
in
変更された型
単一選択肢列を読み出す時と同じように、[DataverseのURL]と[ローカル選択肢列が格納されているテーブル名]をそれぞれ置き換えれば、展開された Column1ステップでそのテーブルに使われているグローバルとローカルの複数選択肢列が表示されます。
後の手順も一緒です。
単一選択肢列と複数選択肢列を読み出す時の違い
違いはAPIで読み出すAttributesだけです。
単一選択肢列
Microsoft.Dynamics.CRM.PicklistAttributeMetadata
複数選択肢
Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata
まとめ
とても面倒
選択肢列はDataverseでよく使われる要素でありながら、なんとPower Queryで読み出す方法の書かれたリファレンスがLearnにありません。
Dataverseを推すならもっと簡単に読み出せるようにして欲しいですね。
すごく面倒
後述の参考にした情報などを探さず独力で出来た人はすごいと思います。
参考にした情報
https://hatfullofdata.blog/power-bi-dataverse-choices-and-choice-column/
https://community.fabric.microsoft.com/t5/Power-Query/Getting-Local-Choice-column-labels-Multi-select/td-p/3595704
https://gist.github.com/expiscornovus/55439b541ed0dc97a597ff16ba3d8ea0

