Power Query 実務テンプレ | クレンジング(前処理)系:主キー重複検出

Excel VBA Power Query M Formula Language
スポンサーリンク

ゴールのイメージをそろえる

今回のテーマは「Power Query で “主キーが重複している行だけをきれいに炙り出す” 実務テンプレ」です。

ここでいう「主キー」は、
「この列(または列の組み合わせ)は、本来1テーブル内で一意であるべき」という“行の識別子”です。
顧客コード、受注番号+行番号、社員番号、商品コード+有効開始日…みたいなやつですね。

やりたいことはシンプルで、
「本来一意であるべき主キーが、どこで・どのくらい・どんな内容で重複しているかを、機械的に検出する」
これをテンプレとして持っておくと、データ品質チェックの“土台”が一気に安定します。


まず「主キー重複」とは何かを具体化する

例題データをイメージする

次のような受注明細テーブルを想像してください。

受注番号行番号商品コード数量金額
10011A001101200
10012A0025800
10021A0033600
10021A0042400
10031A0051200

ここで「主キー」として定義したいのは、

受注番号 + 行番号

です。

本来、「受注番号+行番号」の組み合わせは一意であるべきなのに、
上の例では「1002 + 1」が2行あります。

つまり、「主キー重複」が起きている状態です。
今回のゴールは、この「1002 + 1 の2行」を、Power Query で確実に炙り出すことです。


基本の考え方:主キーでグループ化して「件数>1」を探す

Group By で「主キーごとの件数」を数える

主キー重複を見つける一番素直な方法は、

主キーでグループ化する
各グループの件数を数える
件数が 2 以上の主キーだけを抽出する

という流れです。

まずは「主キーごとの件数」を数えるクエリから組み立てます。

let
    Source = ・・・前のステップ・・・,

    Grouped =
        Table.Group(
            Source,
            {"受注番号", "行番号"},
            {
                {
                    "件数",
                    each Table.RowCount(_),
                    Int64.Type
                }
            }
        )
in
    Grouped
Power Query

この結果は、こんなイメージになります。

受注番号行番号件数
100111
100121
100212
100311

ここで「件数 = 2」の行が、「主キーが重複しているキー」です。


ステップ1:主キー重複しているキーだけを抽出する

件数 > 1 の行だけ残す

先ほどの Grouped テーブルから、「件数 > 1」の行だけを残します。

let
    Source = ・・・前のステップ・・・,

    Grouped =
        Table.Group(
            Source,
            {"受注番号", "行番号"},
            {
                {
                    "件数",
                    each Table.RowCount(_),
                    Int64.Type
                }
            }
        ),

    DuplicatedKeys =
        Table.SelectRows(
            Grouped,
            each [件数] > 1
        )
in
    DuplicatedKeys
Power Query

この DuplicatedKeys には、こういう行だけが残ります。

受注番号行番号件数
100212

つまり、「主キー(受注番号+行番号)が重複しているキーの一覧」です。
これだけでも「どのキーが壊れているか」は分かりますが、
実務では「そのキーに紐づく元の明細行」も見たいことが多いので、もう一歩進めます。


ステップ2:重複している主キーに該当する“元の行”を全部取り出す

DuplicatedKeys を使って、元テーブルをフィルタする

やりたいことは、

元の明細テーブルから、「主キーが DuplicatedKeys に含まれている行だけ」を抽出する

ということです。
これは、JOIN(結合)を使うときれいに書けます。

let
    Source = ・・・前のステップ・・・,

    // 1) 主キーごとの件数を集計
    Grouped =
        Table.Group(
            Source,
            {"受注番号", "行番号"},
            {
                {
                    "件数",
                    each Table.RowCount(_),
                    Int64.Type
                }
            }
        ),

    // 2) 件数 > 1 の主キーだけ抽出
    DuplicatedKeys =
        Table.SelectRows(
            Grouped,
            each [件数] > 1
        ),

    // 3) 元テーブルと重複キー一覧を結合して、重複行だけ取り出す
    Joined =
        Table.NestedJoin(
            Source,
            {"受注番号", "行番号"},
            DuplicatedKeys,
            {"受注番号", "行番号"},
            "Dup",
            JoinKind.Inner
        ),

    // 4) 結合結果から、元の列だけを展開
    Expanded =
        Table.RemoveColumns(
            Joined,
            {"Dup"}
        )
in
    Expanded
Power Query

この Expanded が、「主キー重複している行だけを集めたテーブル」になります。

先ほどの例なら、こうなります。

受注番号行番号商品コード数量金額
10021A0033600
10021A0042400

これで、「どの主キーが重複していて、そのキーに対してどんな行が複数存在しているか」が一目で分かります。


もう少しコンパクトに書くバージョン

Group By の中で「元の行も一緒に持っておく」パターン

さっきは「キー一覧」と「元テーブル」を JOIN しましたが、
Group By の時点で「元の行をまとめて持っておく」書き方もあります。

let
    Source = ・・・前のステップ・・・,

    Grouped =
        Table.Group(
            Source,
            {"受注番号", "行番号"},
            {
                {
                    "行一覧",
                    each _,
                    type table [受注番号=Int64.Type, 行番号=Int64.Type, 商品コード=text, 数量=Int64.Type, 金額=Int64.Type]
                },
                {
                    "件数",
                    each Table.RowCount(_),
                    Int64.Type
                }
            }
        ),

    DuplicatedOnly =
        Table.SelectRows(
            Grouped,
            each [件数] > 1
        ),

    Expanded =
        Table.ExpandTableColumn(
            DuplicatedOnly,
            "行一覧",
            {"受注番号", "行番号", "商品コード", "数量", "金額"},
            {"受注番号", "行番号", "商品コード", "数量", "金額"}
        )
in
    Expanded
Power Query

流れを言葉で整理すると、

Group By で「主キーごとに元の行をまとめた小テーブル(行一覧)」と「件数」を作る
件数 > 1 のグループだけ残す
行一覧を展開して、元の明細行をすべて取り出す

という形です。

JOIN を使うか、Group By の中で完結させるかは好みですが、
「主キー重複検出」という意味ではどちらも同じ結果になります。


重要ポイント1:主キー列は「クレンジング済み」であることが前提

トリム・全角半角・NULL/空白の違いで“ニセ一意”が発生する

主キー重複検出をする前に、必ず意識してほしいのがこれです。

” C001″ と “C001″(前後空白)
“C001” と “C001”(全角)
“” と null(空文字と NULL)

これらは、Power Query 的には全部「別の値」です。
つまり、クレンジング前だと、

見た目は同じ顧客コードなのに、主キーとしては別扱い
→ 一意に見えるけど、実は“ニセ一意”

という状態が簡単に発生します。

なので、主キー重複検出の前に、

前後トリム(全列トリムテンプレ)
全角→半角(コード系列)
空白→NULL または NULL→空白 の統一

などを済ませておくことが、めちゃくちゃ重要です。
「主キーはクレンジング済みの値で判定する」——ここは強く意識しておいてください。


重要ポイント2:「主キー重複を検出する」のか「重複行を除外する」のか

検出と除外は、目的が違う

今回のテーマは「主キー重複検出」です。
つまり、

どの主キーが壊れているかを知りたい
壊れている行を一覧にして、担当者に確認・修正してもらいたい

という“監査・品質チェック”寄りの目的です。

一方で、「重複行を除外して一意なテーブルにしたい」というニーズもあります。
その場合は、Table.Distinct や「ソート+Group By で代表行だけ残す」など、
“どの行を残すか”を決めるロジックが必要になります。

主キー重複は、

検出して報告するのか
自動で解消(どれか1行だけ残す)するのか

どちらをやりたいのかを最初に決めておくと、クエリ設計がブレません。


重要ポイント3:複合キー(複数列主キー)の扱いに慣れておく

1列主キーと複数列主キーの違いは「列リスト」だけ

受注番号だけが主キーのテーブルもあれば、
受注番号+行番号、顧客コード+有効開始日、社員番号+年月…のように、
複数列で主キーを構成するテーブルも多いです。

Power Query 的には、

Table.Group のキー列リスト
Table.NestedJoin のキー列リスト

に複数列を渡すかどうかだけの違いです。

{“受注番号”} なら単一キー
{“受注番号”, “行番号”} なら複合キー

という感覚に慣れておくと、どんな主キーでも同じテンプレで扱えるようになります。


実務テンプレとしてのまとめ

「主キー重複検出」の本質は、次の2ステップです。

主キーで Group By して「件数」を数え、件数 > 1 のキーを“重複キー”として抽出する。
その重複キーに紐づく元の明細行を、JOIN または Group By の展開で一覧にする。

そして、もっと重要なのはその前段階——
主キー列に対して、トリム・全角→半角・NULL/空白統一などのクレンジングを済ませておくことです。

ここまでのあなたの前処理テンプレ(トリム、全角→半角、NULL 統一、数値・日付変換)と、
今回の「主キー重複検出」を組み合わせると、
“壊れたキーを見逃さないデータ品質チェック”を、Power Query だけでかなり高いレベルで回せるようになります。

タイトルとURLをコピーしました