Power BIでJSONを扱うとき、最初にぶつかる壁はだいたい同じです。
取り込めたのに「Record」「List」だらけで表にならない。展開したら列が増えすぎる。配列が二重三重で迷子になる。APIのJSONは日によって項目が増減してエラーになる。
でも、Power Queryにはネスト構造を表に変換するための“型”があります。
ポイントは、JSONの構造を見極めて「Recordは列に、Listは行に」を徹底し、段階的に整形すること。この記事では power bi json 取り込み の実務で使える、手順と設計のコツをまとめます。
まず押さえる:JSONは2種類の箱がある
Power QueryでJSONを読むと、だいたい次のどちらか(または混在)で出てきます。
Record
キーと値の集合。表でいうと「1行の横持ち」に近い存在です。
展開すると列が増えます。
List
配列。表でいうと「縦に並ぶ複数行」に近い存在です。
表に変換すると行が増えます。
ここを取り違えると、いつまで経っても表になりません。
迷ったら、最初にプレビューで Record / List の表示を見て、どっちを“列”にして、どっちを“行”にするか決めます。
取り込みの入り口:ファイルとAPIで考え方が少し違う
1) ローカルJSONファイルから取り込む
基本は「データの取得」からJSONを選ぶだけでOKです。
Power Queryの裏側では Json.Document が使われています。
2) Web APIのJSONから取り込む
Web.Contentsで取得してJson.Documentで解釈します。
APIの場合は、認証・ページング・レート制限・スキーマ変化が実務の論点になります。
最初のうちは、同じ変換ロジックでも「入力が安定しているファイル」から練習すると、理解が早いです。
手順全体の地図:ネストJSONを表にする標準フロー
Power Queryでの定番フローはこれです。
-
JSONを読む(Json.Document)
-
ルートのRecord/Listを確認する
-
ListならTableに変換(Table.FromList)
-
Recordなら列として展開(Table.ExpandRecordColumn)
-
さらに内側にRecord/Listがあれば、同じことを繰り返す
-
必要な粒度まで展開できたら、列名・型・不要列削除を整える
-
参照用のディメンションと、明細用のファクトに分ける(必要なら)
この順番を守ると、作業が“破綻しにくい”です。逆に、型指定や列名整理を先にやると、後の展開でやり直しになりやすいです。
実践:よくあるJSONパターン別の変換手順
ここからは「どう展開するか」をパターンで整理します。
パターンA:ルートがList(配列)で、各要素がRecord
最もよくある形です。たとえばAPIが「ユーザー一覧」を返すようなケース。
変換の考え方
・Listは行になる
・要素のRecordを展開して列にする
手順
-
Listをテーブル化
-
できた1列(Column1など)をRecordとして展開
-
型と列名を整える
Mの例
let
Source = Json.Document(File.Contents("C:\data\users.json")),
AsTable = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
Expanded = Table.ExpandRecordColumn(AsTable, "Column1", {"id","name","email","createdAt"}, {"id","name","email","createdAt"})
in
Expanded
ここまでは簡単ですが、次のパターンから一気に難しく見えます。
パターンB:Recordの中にListがあり、さらにListの中にRecordがある
たとえば「注文(orders)」の中に「注文明細(items)」があるようなJSONです。
変換の考え方
・上位(注文)をまず表にする
・明細(items)は別テーブルにする発想が強い
・itemsはListなので行にする(展開すると注文明細行が増える)
手順(おすすめ構成)
-
ordersを注文ヘッダとして表にする
-
itemsは別クエリに参照して、注文明細として展開する
-
両者をOrderIdなどでリレーションする
注文ヘッダ側(例)
let
Source = Json.Document(File.Contents("C:\data\orders.json")),
Orders = Source[orders], // Recordの中からorders(List)を取り出す例
T0 = Table.FromList(Orders, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
T1 = Table.ExpandRecordColumn(T0, "Column1", {"orderId","orderDate","customer","items"}, {"orderId","orderDate","customer","items"}),
Customer = Table.ExpandRecordColumn(T1, "customer", {"customerId","customerName"}, {"customerId","customerName"})
in
Customer
この時点で items 列はまだListのまま残ります。ここで無理に同じクエリで全展開すると、編集も保守も重くなりがちです。明細は別クエリで作るのが安定します。
注文明細側(itemsを行にする例)
let
Source = Json.Document(File.Contents("C:\data\orders.json")),
Orders = Source[orders],
T0 = Table.FromList(Orders, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
T1 = Table.ExpandRecordColumn(T0, "Column1", {"orderId","items"}, {"orderId","items"}),
// items(List)を行に展開
ItemsExploded = Table.ExpandListColumn(T1, "items"),
// itemsの各要素(Record)を列に展開
ItemsExpanded = Table.ExpandRecordColumn(ItemsExploded, "items", {"sku","qty","price"}, {"sku","qty","price"})
in
ItemsExpanded
ここで大事なのは、Listの展開は行数を増やす操作だということです。
itemsをExpandListColumnした瞬間に「注文×明細」の粒度になります。これが“正しい粒度”なのか、先に判断してから進めます。
パターンC:Listの中にListがある(二重配列)
二重配列は、フラットな表にするときの落とし穴です。
どの階層を行にするかを決めないと、意図せず行数が爆発します。
コツ
・外側のListをまず行にする
・内側のListは「別テーブル」か「後で必要な列だけ拾う」
・どうしても1表にするなら、内側Listを展開して“最終粒度”に合わせる
Power Queryは展開が容易な反面、やりすぎると一瞬で巨大化します。目的の粒度を先に決めてから展開します。
パターンD:Recordのキーが可変(列が日によって増減する)
APIでありがちです。昨日はあったキーが今日は無い、今日だけ新キーが増えた、など。
失敗例
ExpandRecordColumnで固定のフィールド名を指定していると、キーが無い日にエラーになりやすい。
対策1:存在しないフィールドはnullにする
Recordから安全に取り出す関数を使います。
// Record.FieldOrDefaultを使う例(列追加で対応)
let
r = [Column1],
v = Record.FieldOrDefault(r, "newField", null)
in
v
対策2:列名リストを動的に作って展開する
全件を走査して、存在するキーを集めて展開します。
let
Source = Json.Document(File.Contents("C:\data\data.json")),
T0 = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
Records = Table.Column(T0, "Column1"),
AllFields = List.Union(List.Transform(Records, each Record.FieldNames(_))),
Expanded = Table.ExpandRecordColumn(T0, "Column1", AllFields, AllFields)
in
Expanded
この方法は強いですが、データ量が大きいと処理が重くなる場合があります。重いと感じたら「本当に必要なキーだけ」に絞る、または上流でスキーマを固定するのが現実的です。
パターンE:null・空配列・型ブレが混ざる
JSONは型がブレやすいです。ある行は数値、別の行は文字列、別の行はnull、など。
コツ
・型の変換は最後に寄せる
・変換は try で守る
・エラーをnullに落とすか、エラーログ列を作る
let
Added = Table.AddColumn(Prev, "qty_num", each try Number.From([qty]) otherwise null, type number)
in
Added
型変換エラーをそのまま放置すると、更新のたびに止まります。更新を止めない設計が大事です。
“表”にした後の整理で差がつく:設計のベストプラクティス
ここからが実務で効く部分です。JSONを展開できても、使えるモデルになっていなければ意味がありません。
1) 展開は段階的に、不要列は早めに捨てる
JSONを全展開してから削ると、処理が重くなります。
「使わない列」「使わない階層」は早めに切ります。
・まず上位Recordを展開
・必要な列だけ残す
・次の階層へ進む
この順番にすると、更新が速くなりやすいです。
2) 粒度を混ぜない:ヘッダと明細は分ける
注文ヘッダに明細を展開し続けると、同じヘッダ情報が行ごとに重複します。
Power BIのモデルとしては、ヘッダ(ディメンション寄り)と明細(ファクト)を分けたほうが自然です。
・orders(ヘッダ)テーブル
・order_items(明細)テーブル
・customers(顧客)テーブル
JSONの構造に引っ張られすぎず、分析しやすい形に寄せます。
3) 変換ステップに名前を付けて、壊れたときに直せるようにする
Power Queryは手順が増えがちです。
ステップ名が全部「Changed Type」だと、後で自分が困ります。
・Source
・RootExpanded
・ItemsExploded
・ItemsExpanded
・TypesApplied
こういう名前にしておくと、エラーの位置が一瞬で分かります。
4) 1つの巨大クエリにしない:参照クエリで分割する
おすすめの分割パターンです。
・Raw_JSON:Json.Documentまで(整形しない)
・Stg_Orders:注文ヘッダ向け
・Stg_OrderItems:注文明細向け
・Dim / Fact:モデル投入用(列名・型・キー整備)
Rawを残すと、スキーマ変化が起きたときの原因追跡が楽になります。
5) APIの場合の注意:更新の安定性を最優先にする
APIから取り込む場合、次が実務の地雷です。
・ページングがあるのに1ページしか取れていない
・認証が期限切れで止まる
・レート制限で更新が落ちる
・レスポンスが一時的に欠けている
Power Queryで頑張りすぎるより、次のような考え方が安定します。
・取得は最小限にして、整形はPower Queryでやる
・ページングは関数化して見通しを良くする
・エラーはtryで握り、欠損時は空テーブルを返す設計にする
つまずきポイントQ&A(現場で多い順)
Q1:展開したら列が増えすぎて訳が分からない
A:最終的に分析に必要な粒度を決めて、そこまでしか展開しない。上位3階層だけで十分なことが多いです。明細が必要なら別テーブルに分けます。
Q2:Expandでエラーになる日がある
A:スキーマ変化か欠損です。動的フィールドの展開、またはFieldOrDefaultでの防御を入れます。型変換も最後に回します。
Q3:更新が遅い
A:不要列を早めに削る、巨大な動的展開を避ける、参照クエリで分割する。APIなら取得回数を減らす(1回の呼び出しで必要分を取る)設計が効きます。
Q4:どこまで型を設定すべき?
A:分析で使う列だけでOKです。文字列のままでも問題ない列に無理な型変換をかけると、更新停止の原因になります。
まとめ:ルールはシンプル、勝負は手順と粒度
power bi json 取り込み を成功させるコツは、特殊な技よりも次の基本に尽きます。
・Recordは列にする
・Listは行にする
・展開は段階的に進める
・ヘッダと明細は分ける
・スキーマ変化と型ブレを前提に守る
この型を持っていれば、どんなネストJSONでも「どこを行にして、どこを列にして、どこで止めるか」が判断できるようになります。まずは、自分のJSONで一番内側の明細(List)を見つけて、そこを表にするところから始めてみてください。そこができると、あとは同じ手順の繰り返しです。
コメント