Power BI の Power Query でテーブルを resultid でマージ(結合)したのに、一部だけ値が入らず Null になる——しかもフィルターで見ると同じIDが両方に存在し、Excel の XLOOKUP では引ける。こうした“結合できない”現象は、結合キーの実体差か、適用ステップ(処理順)の罠で起きることがほとんどです。原因の切り分けと、確実に直す手順をまとめます。
症状:同じ resultid のはずなのに、マージ結果が Null になる
Power Query の「マージ」は、指定したキー列が一致する行を突き合わせて、もう片方のテーブルの列を付与する機能です。典型的なトラブルは次の形で現れます。
| 状況 | 見える現象 | 判断を誤りやすいポイント |
|---|---|---|
| テーブル1に対してテーブル2を resultid で左外部結合 | 特定行だけテーブル2由来の列が Null | 「一部だけ」なのでバグに見えやすい |
| 手動フィルターで resultid を確認 | 両テーブルに同じ resultid があるように見える | “見た目”と“実体”が違うことがある |
| Excel の XLOOKUP で参照 | 問題なく値が返る | Excel と Power Query で一致判定の前提が異なる |
Power Query のマージは基本的に完全一致で、さらにステップ実行(上から順番)で処理されます。つまり、見た目が同じでも実体が違う、またはマージの時点で対象行が存在していない、というだけで Null が発生します。
まず理解したい:Power Query のマージは「完全一致」+「その時点のテーブル」
Power Query は「適用したステップ」に書かれた順番で、テーブルを変形していきます。マージも例外ではなく、マージのステップが実行された時点で存在していた行にしか結合結果は付きません。
この前提を押さえると、原因は大きく2系統に整理できます。
- 結合キーの不一致:見た目は同じ resultid でも、空白・不可視文字・大小文字・型などが違い、完全一致になっていない
- ステップ順の問題:マージ後に Append(追加)などで行が増え、追加された行には結合結果が付与されない
原因の定番:結合キー(resultid)が“見た目は同じでも実体が違う”
マージが一部だけ失敗する場合、最初に疑うべきは結合キーの不一致です。特に、CSV/ログ/外部システム由来のIDは「人間の目で区別できない差」が混ざりやすいです。
よくある不一致パターン
| パターン | 見た目 | 実体の例 | 対処 |
|---|---|---|---|
| 前後の空白 | 同じに見える | “ABC123” と “ABC123 “ | Trim(前後空白除去) |
| 不可視文字(改行・タブなど) | 同じに見える | “ABC123” と “ABC123(末尾に改行)” | Clean(制御文字除去) |
| ノーブレークスペース(NBSP) | 普通の空白に見える | U+00A0 が混入 | 置換で除去(後述) |
| 大小文字 | 人によっては気づかない | “abc123” と “ABC123” | Upper/Lower で統一 |
| 型の違い(数値 vs 文字列) | 表示は同じでも一致しないことがある | 123(数値)と “123”(文字) | 両方同じ型に揃える |
| 先頭ゼロ | どちらも「123」に見えることがある | “00123” と 123 | 必ずテキストとして扱う |
| 全角/半角・記号の揺れ | パッと見で気づきにくい | “ABC-123” と “ABC-123” | 正規化ルールを決める |
UI操作でできる最低限の正規化(Trim / Clean / 型合わせ)
まずは Power Query エディターで、結合キー列(resultid)に対して次を実行します。
- 両テーブルで、結合キー列(resultid)を選択
- 変換タブ → 書式 → トリム
- 変換タブ → 書式 → クリーン
- ホームタブ → データ型で両テーブルとも同じ型(推奨:テキスト)にする
- 更新(Refresh)してマージを再テスト
これだけで直るケースも多いですが、NBSP(ノーブレークスペース)や、混在した型・先頭ゼロなどが絡むと、UI操作だけでは取り切れないことがあります。
“強めの正規化”をする:結合専用の正規化列を作ってからマージする
結合キーを直接いじるのが不安な場合は、結合専用の正規化列を両テーブルに作り、その列同士でマージするのが安全です。以下は、よくある汚れ(空白・制御文字・NBSP・大小文字)をまとめて処理する例です。
let
NormalizeKey = (v as any) as text =>
let
t0 = Text.From(v),
t1 = Text.Trim(t0),
t2 = Text.Clean(t1),
// NBSP(160) を除去(Text.Clean では残ることがある)
t3 = Text.Replace(t2, Character.FromNumber(160), ""),
t4 = Text.Upper(t3)
in
t4
in
NormalizeKey
この関数を使って、テーブル1/テーブル2の両方に正規化列を追加します。
= Table.AddColumn(前のステップ, "resultid_norm", each NormalizeKey([resultid]), type text)
以降は、マージのキーを resultid ではなく resultid_norm に切り替えます。IDのような「揺れてほしくない列」は、最初に正規化して固定すると運用が安定します。
一致しているかを“数値で”確認する小技(目視に頼らない)
「本当に同じ?」を目視ではなく数値で確認すると、原因に早く辿り着けます。例えば次をテーブル1側に追加し、Null になっている行で差を見ます。
- 文字数:
Text.Length(Text.From([resultid])) - 正規化後の文字数:
Text.Length([resultid_norm])
同じに見えるのに文字数が違えば、空白や不可視文字が混ざっている可能性が高いです。
今回の本命:マージの後に Append(追加)していたため、追加行に結合結果が付いていない
「同じ resultid が両方にあるのに Null」という現象で、意外と多いのが「マージした後に Append(追加)で行が増えている」ケースです。Power Query はステップ順に処理するため、次のような流れだと問題が起きます。
| ステップ | 処理 | その時点で存在する行 | 結果 |
|---|---|---|---|
| ステップA | テーブル1 と テーブル2 を resultid でマージ | テーブル1に元からある行だけ | この行だけ結合列が埋まる |
| ステップB | Append(追加)で別テーブルの行を後から足す | ステップBで新しい行が増える | 追加された行はマージ対象外なので結合列が Null のまま |
この状態だと、フィルターで見ると resultid は両方に存在します。しかし、マージが実行された時点では「追加行がまだ存在していない」ため、後から入った行には結合結果が付かない、という理屈になります。Power Query の動きとしては正常です。
解決策:Append を先に、Merge を後に(または Merge を後段に作り直す)
対処は大きく3パターンあります。実務での扱いやすさ・保守性の観点で、まずはAppend → Mergeを第一候補にするのがおすすめです。
パターン:Append(追加)を先にやってから、まとめて Merge(推奨)
「最終的に存在する全行」を先に1つのテーブルにまとめ、その結果に対してマージします。
- テーブル1 と 追加元テーブル(例:テーブル1_追加)を Append して AllRows を作る
- AllRows と テーブル2 を resultid(または resultid_norm)で Merge する
- 必要な列だけ展開(Expand)する
// AllRows(Append 結果)
AllRows = Table.Combine({テーブル1, テーブル1_追加})
// マージ(左外部結合)
Merged =
Table.NestedJoin(
AllRows, {"resultid_norm"},
テーブル2, {"resultid_norm"},
"T2", JoinKind.LeftOuter
)
// 展開(必要列だけ)
Expanded =
Table.ExpandTableColumn(Merged, "T2", {"colA","colB"}, {"colA","colB"})
この形にすると、「後から追加された行にも同じ結合処理が適用される」ため、Null 問題が素直に解消します。
代替:Append 後に Merge をやり直す(マージのステップを後段へ)
既存のクエリを大きく崩したくない場合は、Append の後にマージをやり直す方法もあります。ポイントは、最終テーブルの直前に Merge が来るようにすることです。
- GUI上でステップをドラッグして並べ替えることはできないため、一般的には「Append後のテーブルを新しいクエリとして参照し、そのクエリで Merge する」形が安全です。
- Advanced Editor(詳細エディター)で M を直接編集する場合は、参照ステップ名の整合に注意します。
代替:追加元それぞれに Merge を適用してから Append する
追加元が複数あり、各テーブルで前処理が異なる場合は、各テーブルでマージを完了させてから Append する設計も有効です。
- メリット:テーブルごとの前処理(型変換、列名の揃え方)が自由
- 注意点:Merge が複数回走るので、データ量が大きいと更新時間が伸びることがある
切り分け実践:どこで Null が生まれているかを特定する
「キー不一致なのか」「ステップ順なのか」を最短で見分けるために、次の切り分けが効きます。
マージ直後の“ネストされたテーブル”で一致/不一致を可視化する
Power Query のマージは、展開前は「一致した行の集合(テーブル)」が1セルに入った状態になります。ここで一致件数を数えると、結合できているかが一発で分かります。
= Table.AddColumn(マージ直後のステップ, "match_count", each Table.RowCount([T2]), Int64.Type)
match_count が 0 の行は、結合できていません。ここで 0 行が出るなら「キー不一致」が濃厚です。一方、Append の後に Null が増えるなら「ステップ順」が濃厚です。
Left Anti Join(左反結合)で“マッチしないIDだけ”抽出する
UIでも簡単にできます。
- ホームタブ → クエリのマージ
- 結合キーを指定
- 結合の種類で 左反結合(左側のみ) を選ぶ
これで「テーブル1にはあるが、テーブル2にはない resultid だけ」を一覧化できます。そこに並んだ resultid を、正規化前後で比較すると、ズレの正体が見えます。
結合の種類(JoinKind)の選び方:目的に合わないと“欠損に見える”
マージでよく使うのは左外部結合ですが、目的によっては別の種類の方が切り分けに向いています。特に「原因調査」では反結合が強力です。
| 結合の種類 | 用途 | この問題での使いどころ |
|---|---|---|
| 左外部(Left Outer) | テーブル1を基準に、テーブル2の情報を付与 | 本番の結合(値を持ってきたい) |
| 内部(Inner) | 一致した行だけ残す | 「一致している行だけ」を確認したい時 |
| 完全外部(Full Outer) | 両方の差分を含めて残す | 「どっちにしか存在しないIDがあるか」を俯瞰したい時 |
| 左反結合(Left Anti) | テーブル1にだけある行を残す | “マージできないID”を抽出して原因を見る |
| 右反結合(Right Anti) | テーブル2にだけある行を残す | テーブル2側の不足や欠落を確認したい時 |
ID列の結合で「曖昧一致(Fuzzy)」を使うのは基本的におすすめしません。IDは“似ている”ではなく“同一”であるべきなので、まずは正規化で完全一致に寄せる方が安全です。
Excel の XLOOKUP では引けるのに、Power Query のマージでは引けない理由
Excel と Power Query は似ているようで、データの扱いが少し違います。これが「Excel では取れるのに Power Query では Null」という体感差につながります。
| 観点 | Excel(XLOOKUP) | Power Query(Merge) |
|---|---|---|
| 型の揺れ | セルの表示・自動変換で“同じに見える”ことがある | 列のデータ型が明確で、型が違うと一致しにくい |
| 空白・不可視文字 | 気づかず作業が進みやすい(セルの見た目が同じ) | 完全一致のため、1文字違えばマッチしない |
| 処理の順序 | 関数は参照元が変わると再計算される | ステップ順に実行され、後から Append した行には前段の Merge が適用されない |
つまり「Power Query が間違っている」というより、Power Query の方が“厳密に一致を要求する”場面がある、という理解が近いです。厳密さは、再現性と自動化に強いというメリットでもあります。
再発防止:マージ設計を安定させるコツ
同じトラブルを繰り返さないためには、Power Query のクエリ設計を少しだけ意識すると効果が大きいです。
「キーを最初に整える」:正規化列はステージングで作る
結合キーは、後工程で触るほど事故が増えます。おすすめは、取り込み直後のステージングクエリ(例:stg_テーブル1、stg_テーブル2)で resultid_norm を作り、以降はその列だけを結合に使う運用です。
「Append は早め、Merge は遅め」:最終形に近いところで結合する
Append(行の追加)や列のユニオンは、行数・列構造を大きく変えます。形が変わる処理は先に終わらせ、参照(ディメンション)を付ける Merge は後ろに寄せると、今回のような「後から増えた行が空欄になる」事故を避けられます。
「自動生成された Changed Type」を見直す
Power Query は取り込み時に Changed Type(型の変更)ステップを自動で作ることがあります。Append や列追加の前後で型が揺れると、結合キーが意図せず数値化されたり、テキスト化されたりして、マージ不一致の温床になります。更新が不安定な場合は、次を確認します。
- 結合キー(resultid)が途中で数値型になっていないか
- Append の後に型が Any になっていないか
- 型変換のステップが「Append の前」にあり、追加行に適用されていない構造になっていないか
大規模データの更新を重くしないための実務ポイント
- 不要列は早めに削除:マージ前に列数を減らすと、結合の負荷が下がります。
- 結合キーの計算は一度で済ませる:正規化列を作ったら、それを基準に統一します。
- 重複キーは事前に整理:テーブル2側が1ID複数行だと展開で行が膨らみやすいので、必要ならグループ化で1行化します。
- Table.Buffer の多用は避ける:更新が遅くなる原因になることがあるため、目的が明確な場合だけ使います。
確認用チェックリスト(同じIDなのに Null のとき)
| チェック項目 | 確認方法 | 改善策 |
|---|---|---|
| 両テーブルの resultid の型は同じか | 列ヘッダー左のアイコン(ABC/123) | 両方テキストに統一 |
| 前後空白・制御文字が混入していないか | Text.Length で差を見る | Trim / Clean を適用 |
| NBSP が混入していないか | Trim/Clean後も一致しない行を抽出 | Character.FromNumber(160) を置換 |
| 先頭ゼロが消えていないか | 取り込み直後の値と比較 | 最初からテキストとして扱う |
| マージの時点で、対象行は存在しているか | 適用ステップの順序を確認 | Append を先にしてから Merge |
| マージ直後の一致件数は 0 になっていないか | Table.RowCount([ネスト列]) | 0ならキー不一致、増えるなら順序問題 |
| テーブル2側に同じ resultid が複数行ないか | グループ化で件数を見る | 重複排除 or 集計して1行化 |
よくある質問
フィルターで同じ resultid が見えるのに、なぜ一致しないのですか?
表示上は同じでも、末尾空白や不可視文字が含まれていることが多いです。まずは Trim/Clean を当て、さらに正規化列(resultid_norm)で比較してください。文字数がズレていたら、目に見えない差が混ざっています。
マージすると一部の行で重複が増えます
テーブル2側に同じ resultid が複数行あると、マージ結果のネストテーブルが複数行になります。展開時に行が増えるのは仕様です。意図しない増加なら、テーブル2を事前にグループ化して1ID=1行に整形してからマージします。
更新のたびに一致したりしなかったりします
取り込み元のデータ型推定や、自動生成された Changed Type が影響している可能性があります。結合キーは常にテキストで固定し、正規化列を作るタイミングを「取り込み直後」に寄せると安定します。
まとめ
- Power Query のマージは完全一致。見た目が同じでも空白・不可視文字・型の違いで一致しない
- Null が一部だけ出るときは、まずresultid の正規化(Trim/Clean/型合わせ/大小統一)を疑う
- マージ後に Appendしていると、追加行には結合結果が付かない。Append→Mergeの順に設計し直す
- マージ直後のネスト列で
Table.RowCountを見ると、原因切り分けが速い

コメント