ゴールのイメージをそろえる
今回のテーマは「Power Query で “主キーが重複している行だけをきれいに炙り出す” 実務テンプレ」です。
ここでいう「主キー」は、
「この列(または列の組み合わせ)は、本来1テーブル内で一意であるべき」という“行の識別子”です。
顧客コード、受注番号+行番号、社員番号、商品コード+有効開始日…みたいなやつですね。
やりたいことはシンプルで、
「本来一意であるべき主キーが、どこで・どのくらい・どんな内容で重複しているかを、機械的に検出する」
これをテンプレとして持っておくと、データ品質チェックの“土台”が一気に安定します。
まず「主キー重複」とは何かを具体化する
例題データをイメージする
次のような受注明細テーブルを想像してください。
| 受注番号 | 行番号 | 商品コード | 数量 | 金額 |
|---|---|---|---|---|
| 1001 | 1 | A001 | 10 | 1200 |
| 1001 | 2 | A002 | 5 | 800 |
| 1002 | 1 | A003 | 3 | 600 |
| 1002 | 1 | A004 | 2 | 400 |
| 1003 | 1 | A005 | 1 | 200 |
ここで「主キー」として定義したいのは、
受注番号 + 行番号
です。
本来、「受注番号+行番号」の組み合わせは一意であるべきなのに、
上の例では「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この結果は、こんなイメージになります。
| 受注番号 | 行番号 | 件数 |
|---|---|---|
| 1001 | 1 | 1 |
| 1001 | 2 | 1 |
| 1002 | 1 | 2 |
| 1003 | 1 | 1 |
ここで「件数 = 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 には、こういう行だけが残ります。
| 受注番号 | 行番号 | 件数 |
|---|---|---|
| 1002 | 1 | 2 |
つまり、「主キー(受注番号+行番号)が重複しているキーの一覧」です。
これだけでも「どのキーが壊れているか」は分かりますが、
実務では「そのキーに紐づく元の明細行」も見たいことが多いので、もう一歩進めます。
ステップ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 が、「主キー重複している行だけを集めたテーブル」になります。
先ほどの例なら、こうなります。
| 受注番号 | 行番号 | 商品コード | 数量 | 金額 |
|---|---|---|---|---|
| 1002 | 1 | A003 | 3 | 600 |
| 1002 | 1 | A004 | 2 | 400 |
これで、「どの主キーが重複していて、そのキーに対してどんな行が複数存在しているか」が一目で分かります。
もう少しコンパクトに書くバージョン
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 だけでかなり高いレベルで回せるようになります。
