Power BI Power Queryのマージ(結合)で同じIDなのにNullになる原因と解決策|Append順序とキー正規化

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)に対して次を実行します。

  1. 両テーブルで、結合キー列(resultid)を選択
  2. 変換タブ → 書式 → トリム
  3. 変換タブ → 書式 → クリーン
  4. ホームタブ → データ型で両テーブルとも同じ型(推奨:テキスト)にする
  5. 更新(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に元からある行だけこの行だけ結合列が埋まる
ステップBAppend(追加)で別テーブルの行を後から足すステップBで新しい行が増える追加された行はマージ対象外なので結合列が Null のまま

この状態だと、フィルターで見ると resultid は両方に存在します。しかし、マージが実行された時点では「追加行がまだ存在していない」ため、後から入った行には結合結果が付かない、という理屈になります。Power Query の動きとしては正常です。

解決策:Append を先に、Merge を後に(または Merge を後段に作り直す)

対処は大きく3パターンあります。実務での扱いやすさ・保守性の観点で、まずはAppend → Mergeを第一候補にするのがおすすめです。

パターン:Append(追加)を先にやってから、まとめて Merge(推奨)

「最終的に存在する全行」を先に1つのテーブルにまとめ、その結果に対してマージします。

  1. テーブル1 と 追加元テーブル(例:テーブル1_追加)を Append して AllRows を作る
  2. AllRows と テーブル2 を resultid(または resultid_norm)で Merge する
  3. 必要な列だけ展開(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. 結合キーを指定
  3. 結合の種類で 左反結合(左側のみ) を選ぶ

これで「テーブル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 を見ると、原因切り分けが速い

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次