大量のデータの中からあるキーとなる値が重複するレコードを抽出したい時があります。
例えばキーとなる値が一意になるように作成したつもりのテストデータが、意図せずに重複している場合など…。
今回はSQLで重複しているレコードを抽出する方法の紹介です。
目次
サンプルデータの作成
重複するレコードを抽出するためのサンプルデータを作成します。
キーとなる値を保持する列として電話番号を持ち、電話番号の所有者を表す氏名の列も持つ電話番号リストテーブルを作成します。
テーブルのCREATEとデータのINSERT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-- テーブルの作成 CREATE TABLE TEL_LIST ( TEL NVARCHAR(20) NOT NULL, FULL_NAME NVARCHAR(50) NOT NULL ); -- データの挿入 INSERT INTO TEL_LIST (TEL, FULL_NAME) VALUES ('09012345678', 'テスト 氏名1'), ('09022222222', 'テスト 氏名2'), ('09033333333', 'テスト 氏名3'), ('09044444444', 'テスト 氏名4'), ('09055555555', 'テスト 氏名5'), ('09066666666', 'テスト 氏名6'), ('09012345678', 'テスト 氏名7'), ('09088888888', 'テスト 氏名8'); |
データは「テスト 氏名1」と「テスト 氏名7」の電話番号を重複させておきます。
上記のTEL_LISTテーブルへのデータのインサートは、複数のレコードを1回のSQLで一括挿入する形式で記述しています。
1回のINSERTで複数行のレコードをテーブルに一括で挿入する方法については、以下の記事を参照してください。
重複レコードを抽出する
重複するレコードを抽出するにはGROUP BY句を使用します。
GROUP BY句にで一意となるキーを指定して取得します。
1 2 3 4 5 6 7 8 |
-- 電話番号(TEL)をキーにグループ化 SELECT TEL, COUNT(TEL) FROM TEL_LIST GROUP BY TEL |
上記のSQLを実行すると7件のレコードが取得できます。
COUNT関数で電話番号の件数を取得して「09012345678」のレコードが2件あることが確認します。
HAVING句を使用する方法
HAVING句を使用する場合はGROUP BY句でグループ化して、COUNT関数で件数を取得した結果が1より大きいレコード(2件以上あるレコード)をHAVING句の条件に指定します。
HAVING句を使用して重複するレコードを取得するSQLは以下のようになります。
1 2 3 4 5 6 7 8 9 10 |
-- GROUP BYとHAVINGを使用して重複レコードを抽出 SELECT TEL, COUNT(TEL) FROM TEL_LIST GROUP BY TEL HAVING COUNT(TEL) > 1 |
HAVING句に指定した条件によって電話番号が「09012345678」の「テスト 氏名1」と「テスト 氏名7」の2件のレコードが対象になっているのがわかります。
サブクエリにする方法
重複しているレコードの取得はHAVING句を使わずにグループ化した結果をサブクエリとして、サブクエリに自分自身のテーブルを自己結合することで取得することもできます。
以下にGROUP BYの結果をサブクエリにして重複するレコードを取得するSQLを記載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- サブクエリで件数を取得してWHERE句で条件を指定 SELECT TEL_LIST.TEL, TEL_LIST.CNT FROM ( SELECT TEL, COUNT(TEL) AS CNT FROM TEL_LIST GROUP BY TEL ) AS TEL_LIST WHERE TEL_LIST.CNT > 1 |
上記のSQLではグループ化した結果をサブクエリにして(6~14行目)WHERE句の条件に件数が1より大きいレコード(2件以上あるレコード)を指定します。
自己結合して対象のレコードを全て取得
最後に取得した重複レコードの結果と自分自身のテーブル(TEL_LIST)を自己結合して電話番号が重複している人の氏名(FULL_NAME)を取得する例を記載しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
SELECT TEL_LIST.TEL, TEL_LIST.FULL_NAME FROM -- HAVING句を使用した結果をサブクエリにする ( -- GROUP BYとHAVINGを使用して重複レコードを抽出 SELECT TEL, COUNT(TEL) AS CNT FROM TEL_LIST GROUP BY TEL HAVING COUNT(TEL) > 1 ) AS TEL_CNT -- 自己結合 INNER JOIN TEL_LIST ON TEL_CNT.TEL = TEL_LIST.TEL |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
SELECT TEL_LIST.TEL, TEL_LIST.FULL_NAME FROM -- サブクエリで件数を取得 ( SELECT TEL, COUNT(TEL) AS CNT FROM TEL_LIST GROUP BY TEL ) AS TEL_CNT -- 自己結合 INNER JOIN TEL_LIST ON TEL_CNT.TEL = TEL_LIST.TEL AND TEL_CNT.CNT > 1 |
上記のSQLではグループ化したサブクエリの結果表に自分自身のテーブル(TEL_LIST)を電話番号(TEL)をキーにINNER JOINで内部結合しています。
自己結合することで重複している全てのレコードを結果表として取得できます。
自己結合した結果、「テスト 氏名1」と「テスト 氏名7」の2件のレコードが取得されます。