データベースを長く運用していると、アプリのバグやテストデータの混入、複数サーバ間複製のトラブルなどが原因で、重複データが発生することがあります。
しかし、安易に DELETE を実行すると必要なデータまで消えてしまう危険があります。
そこで本記事では、ROW_NUMBER() を使って重複行を安全に削除する方法をわかりやすく解説します。
🎯 本記事のゴール
-
重複データを判定する方法が理解できる
-
削除対象を事前に確認できる
-
ROW_NUMBER()とDELETEの実践手順がわかる
重複データとは?
以下のように、ユニークであるべき項目(メールアドレス、社員番号、商品コードなど)が複数存在する状態です。
| id | name | |
|---|---|---|
| 1 | aaa@test.com | |
| Tanaka | ||
| 2 | aaa@test.com | |
| Tanaka | ||
| 3 | bbb@test.com | |
| Suzuki |
この場合、aaa@test.com が重複行となります。
手順①:重複データの行番号を振る
まずは削除対象となる行を特定します。ROW_NUMBER() OVER (PARTITION BY ...) で同じキーを持つ行に番号を付与します。
実行結果例
| id | rn | |
|---|---|---|
| 1 | aaa@test.com | |
| 1 | ||
| 2 | aaa@test.com | |
| 2 | ||
| 3 | bbb@test.com | |
| 1 |
rn = 1 を残して rn > 1 を削除対象とします。
手順②:削除対象のみを事前確認
※ 実務では必ずこのチェックを推奨
手順③:重複行を削除
🔒 サブクエリが必要な理由
一部DBでは DELETE ... FROM 内で ROW_NUMBER() を直接参照できないため
サブクエリ(派生テーブル)を挟む必要があります。
💡 Delete 実行前の注意点
| 注意点 | 内容 |
|---|---|
| バックアップ取得 | DELETE後は戻せない |
| トランザクション内で実行 | BEGIN / ROLLBACK の利用 |
| WHERE句のチェック | WHERE間違いは致命傷 |
| ORDER BYの基準を決める | 新しいデータを残す/古いデータを残すなど |
例:最新データを残したい場合
DBごとの対応表
| DB | 使用可否 |
|---|---|
| PostgreSQL | ◎ |
| SQL Server | ◎ |
| Oracle | ◎ |
| MySQL 8.0 以降 | ◎ |
| MySQL 5.x | × → サブクエリ+JOINで対応 |
| MariaDB | △ |
※ MySQL 5系の場合は別途記事で解説予定
まとめ
| 結論 | 内容 |
|---|---|
| ROW_NUMBER()は重複削除に最適 | 削除対象を明確化できる |
| いきなりDELETEしない | SELECTで必ず事前確認 |
| ORDER基準を明確にする | 残すデータのルール決め |
安全に確実に重複を削除するには
「削除対象の可視化」→「確認」→「DELETE」
という流れがとても重要です。
