SQLで重複を見つけることは面倒な作業かもしれませんが、データベースを扱う人にとって欠かせないスキルです。重複はデータにエラー、不一致、矛盾を引き起こし、不正確な結果やパフォーマンスの低下につながります。したがって、データの整合性と正確性を確保するために、テーブルから重複を特定して削除することが重要です。
SQLは、要件やテーブルの構造に応じて、データ内の重複を見つけるためのさまざまな方法を提供します。GROUP BYとHAVING句を使用して、特定の列でレコードをグループ化し、カウントや条件に基づいて重複をフィルタリングできます。また、DISTINCT キーワードを使用して一意の値のみを選択し、元のテーブルと比較して重複を特定することもできます。COUNT()、EXISTS、JOINなどの専門的な関数や演算子もあり、より複雑なシナリオで重複を見つけるのに役立ちます。
このガイド全体を通じて、ブラウザで直接重複の検索を練習できるインタラクティブなSQLエクササイズを見つけることができます。データベースのセットアップは不要です。クエリを書いて、即座にフィードバックを受け取り、結果が期待される出力と一致するかどうかを確認してください。
この記事では、シンプルなクエリから高度な方法まで、SQLで重複を見つけるためのさまざまなテクニックを探り、このスキルをマスターするための例とベストプラクティスを提供します。初心者でも経験豊富なSQL開発者でも、このガイドはテーブルから重複を検出して排除することで、データの品質と効率を向上させるのに役立ちます。これらのクエリを練習するには、シンタックスハイライトとオートコンプリート機能を備えたBeekeeper StudioのSQLエディタのような最新のSQLエディタの使用を検討してください。
GROUP BYとHAVING句を使用して重複を見つける
SQLで重複値を見つける1つの方法は、GROUP BYとHAVING句を使用することです。これらの句により、1つ以上の列で同じ値を持つ行をグループ化し、特定の基準に基づいてグループをフィルタリングできます。仕組みは次のとおりです:
- SELECT文を使用して、重複をチェックしたい列を選択することから始めます。
- GROUP BY句を使用して、選択した列で行をグループ化します。
- HAVING句でCOUNT関数を使用して、1行以上のグループをフィルタリングします。これらが重複を含むグループです。
たとえば、「name」と「email」の列を持つ「customers」というテーブルがあるとします。同じメールアドレスで登録したすべての顧客を見つけたいとします。SQLクエリは次のようになります:
SELECT name, email, COUNT(*)
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;
このクエリは顧客をメールアドレスでグループ化し、各グループ内の顧客数をカウントします。HAVING句は顧客が1人だけのグループをフィルタリングし、重複したメールアドレスを持つグループのみを残します。
自分で試してみましょう:
[[ testData.title ]]
usersテーブルをクエリして、1回以上出現するすべてのメールアドレスを見つけてください。emailと各重複メールが出現する回数のcountを返してください。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
GROUP BY句には、COUNTなどの集計関数を使用するものを除いて、選択しているすべての列を含める必要があることに注意してください。そうしないと、クエリはエラーを返します。
もう1つ覚えておくべきことは、COUNT関数はユニークなものだけでなく、各グループ内のすべての行をカウントするということです。したがって、同じ名前とメールアドレスを持つ複数の行がある場合、すべてが重複としてカウントされます。
GROUP BYとHAVING句を使用する以外にも、ORDER BY、WHERE、JOINなどの他のSQL文を使用して、重複の検索をさらに絞り込むことができます。また、一意制約を使用して、そもそも重複値がテーブルに挿入されるのを防ぐこともできます。
全体として、SQLで重複を見つけることは、未処理のデータをクリーンアップしたり、潜在的な人為的エラーやアプリケーションのバグを特定するための強力なツールになり得ます。適切な検索条件を使用し、結果を明確かつ簡潔に出力することで、重複した行や値をすばやく特定し、修正措置を取ることができます。
COUNT関数の使用
SQLで重複を見つける1つの方法は、COUNT関数を使用することです。COUNT関数は、特定の条件を満たすテーブル内の行数をカウントする集計関数です。COUNT関数を使用すると、列内の特定の値の出現回数をカウントし、重複を特定できます。
COUNT関数を使用して重複を見つけるには、重複をチェックしたい列で行をグループ化する必要があります。GROUP BY句は、特定の列の値に基づいて行をグループ化するために使用されます。たとえば、「users」テーブルの「email」列で重複を見つけたい場合は、「email」列で行をグループ化します。
「users」テーブルの「email」列で重複を見つけるためにCOUNT関数を使用するクエリの例を示します:
SELECT email, COUNT(email) as count
FROM users
GROUP BY email
HAVING COUNT(email) > 1;
このクエリでは、GROUP BY句が「email」列で行をグループ化し、COUNT関数が各メールアドレスの出現回数をカウントします。HAVING句は、1回以上出現するメールアドレスのみを表示するように結果をフィルタリングします。
このクエリの結果は、重複があるメールアドレスと各メールアドレスの出現回数を示すテーブルになります。この情報を使用して、テーブルから重複行を特定して削除できます。
COUNT関数を使用することは、SQLで重複を見つけるシンプルで効果的な方法です。列内の重複値をすばやく特定し、テーブルから削除するための措置を取ることができます。
INNER JOIN句の使用
SQLで重複を見つける最も一般的な方法の1つは、INNER JOIN句を使用することです。この句により、共通の列に基づいて2つ以上のテーブルを結合し、両方のテーブルで一致する値を持つ行のみを返すことができます。
INNER JOIN句を使用して重複を見つけるには、重複データを含む列を特定する必要があります。これらの列を特定したら、INNER JOIN句を使用してこれらの列でテーブルを結合できます。
「orders」というテーブルで重複を見つけるためにINNER JOIN句を使用する方法の例を示します:
SELECT o1.order_id, o2.order_id
FROM orders o1
INNER JOIN orders o2
ON o1.customer_id = o2.customer_id
AND o1.order_date = o2.order_date
AND o1.order_id <> o2.order_id;
この例では、INNER JOIN句を使用して「orders」テーブルを自分自身と結合し、「customer_id」と「order_date」列を結合条件として使用しています。「AND」演算子は、行が返されるために3つの列すべてが一致する必要があることを指定するために使用されます。クエリの最後の行「AND o1.order_id <> o2.order_id」は、両方の「order_id」値が同じである行をクエリが返さないことを保証します。
このクエリの結果は、同じ顧客IDと注文日を持つが、異なる注文IDを持つすべての注文のリストになります。これらはさらに調査が必要な重複注文です。
セルフジョインで練習:
[[ testData.title ]]
productsテーブルをクエリして、すべての重複製品レコード(同じproduct_nameとcategory)を見つけてください。重複レコードのみ(最初の出現以外)のproduct_id、product_name、category、priceを返してください。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
このようにINNER JOIN句を使用することは、SQLで重複を見つけるための強力なツールになり得ます。ただし、この方法を使用する際は注意が必要です。誤検出を返したり、複数のテーブルに分散している重複を見逃したりすることが簡単に起こり得るからです。結果を再確認し、GROUP BYやHAVING句などの他の方法を使用して発見を確認することは常に良いアイデアです。
自分で試してみましょう:
[[ testData.title ]]
customersテーブルをクエリして、メールが1回以上出現する顧客の完全なレコード(nameとemail)を返してください。サブクエリを使用して重複メールを識別します。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
セルフジョインを使用して重複ペアを見つける
重複を見つけるためのもう1つの強力なテクニックは、共通の特性を共有するレコードのペアを特定するためにセルフジョインを使用することです。このアプローチは、重複が存在することを単に特定するのではなく、正確な重複ペアを見つけたい場合に特に役立ちます。
セルフジョインは、異なるエイリアスを使用してテーブルを自分自身と結合することを含みます。重複を定義する列で結合し、レコードが自分自身と一致しないことを確認することで、重複ペアを効果的に特定できます。
たとえば、トランザクションテーブルがあり、同じ顧客IDと金額を持つが異なるトランザクションIDを持つトランザクションのペアを見つけたい場合、次のようなセルフジョインを使用できます:
SELECT t1.transaction_id as transaction_id1,
t2.transaction_id as transaction_id2,
t1.customer_id,
t1.amount
FROM transactions t1
INNER JOIN transactions t2
ON t1.customer_id = t2.customer_id
AND t1.amount = t2.amount
AND t1.transaction_id < t2.transaction_id;
このクエリの重要なポイントは次のとおりです:
-
t1とt2は同じテーブルのエイリアスです - 結合条件は、レコードを「重複」にするものを指定します(
customer_idとamount) -
t1.transaction_id < t2.transaction_idは、各ペアを1回だけ取得し、レコードが自分自身と一致しないことを保証します
自分で試してみましょう:
[[ testData.title ]]
transactionsテーブルをクエリして、同じcustomer_idとamountを持つが異なるtransaction_idを持つトランザクションのペアを見つけてください。各重複ペアのtransaction_id1、transaction_id2、customer_id、amountを返してください。各ペアは1回だけ表示してください(A-BとB-Aの両方を表示しないでください)。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
Common Table Expression(CTE)を使用して重複を見つける
SQLで重複を見つける方法の1つは、Common Table Expression(CTE)を使用することです。CTEは、単一のSELECT、INSERT、UPDATE、DELETE、またはCREATE VIEW文の実行スコープ内で定義される一時的な結果セットです。
CTEを使用して重複を見つけるには、ROW_NUMBER()関数を使用できます。この関数は、結果セットのパーティション内の各行に一意の連番を割り当てます。PARTITION BY句はパーティションを定義するために使用される列を指定し、ORDER BY句は各パーティション内の行の順序を指定します。
CTEを使用して重複を見つける例を示します:
WITH CTE AS (
SELECT column1, column2, column3, ROW_NUMBER() OVER(PARTITION BY column1, column2, column3 ORDER BY column1, column2, column3) AS RowNumber
FROM table_name
)
SELECT *
FROM CTE
WHERE RowNumber > 1
この例では、CTEは重複をチェックする列で定義されています。ROW_NUMBER()関数は、結果セットのパーティション内の各行にシーケンス番号を生成するために使用されます。パーティションはPARTITION BY句で指定された列によって定義されます。その後、結果セットはRowNumberが1より大きい行のみを表示するようにフィルタリングされ、重複があることを示します。
ROW_NUMBER()関数内のORDER BY句は、メインのSELECT文のORDER BY句と一致する必要があることに注意してください。そうしないと、結果が正確でない可能性があります。
CTEを使用して重複を見つけることは、Common Table Expressionを使用したグラフ処理のサポートを導入したSQL Server 2017で特に役立ちます。これにより、データ間の関係を含むより複雑なクエリが可能になります。
全体として、CTEを使用してSQLで重複を見つけることは、データ分析と管理のための強力なツールになり得ます。
自分で試してみましょう:
[[ testData.title ]]
Common Table Expression(CTE)を使用してproductsテーブルをクエリし、重複した製品名を見つけてください。CTEは製品名でグループ化して出現回数をカウントし、カウント > 1の製品を選択する必要があります。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
ウィンドウ関数を使用して重複を見つける
SQLで重複を見つける場合、最も効率的な方法の1つはウィンドウ関数を使用することです。ウィンドウ関数は、現在の行に関連する一連の行に対して計算を実行するために使用できる強力なツールです。
ウィンドウ関数を使用して重複を見つけるには、ROW_NUMBER()関数をPARTITION BY句と組み合わせて使用できます。ROW_NUMBER()関数はパーティション内の各行に一意の番号を割り当て、PARTITION BY句は特定の列または列のセットに基づいて行をパーティションにグループ化します。
たとえば、user_id、username、emailの列を含むユーザーテーブルがあるとします。重複したメールを持つすべてのユーザーを見つけるには、次のselect文を使用できます:
SELECT user_id, username, email
FROM (
SELECT user_id, username, email,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY user_id) AS row_num
FROM users
) AS subquery
WHERE row_num > 1;
この例では、PARTITION BY句がemail列で行をパーティション化し、ROW_NUMBER()関数がuser_id列に基づいてパーティション内の各行に一意の番号を割り当てます。WHERE句は、重複したメールを持つ行であるrow_numが1より大きい行以外のすべての行をフィルタリングします。
ROW_NUMBER()で練習:
[[ testData.title ]]
customersテーブルをクエリして、メールが1回以上出現する顧客の完全なレコード(nameとemail)を返してください。サブクエリを使用して重複メールを識別します。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
ウィンドウ関数は、CASE文や集計関数などの他のSQL関数と組み合わせて、より複雑なクエリを作成できることに注意してください。たとえば、CASE文を使用して行を特定のカテゴリにグループ化し、ウィンドウ関数を使用してそのカテゴリ内の重複を見つけることができます。
結論として、ウィンドウ関数を使用してSQLで重複を見つけることは、強力で効率的な方法になり得ます。ROW_NUMBER()関数をPARTITION BY句と組み合わせて使用することで、行をパーティションに簡単にグループ化し、パーティション内の各行に一意の番号を割り当てることができます。これにより、結果から重複行をすばやく特定して削除できます。
自分で試してみましょう:
[[ testData.title ]]
productsテーブルをクエリして、すべての重複製品レコード(同じproduct_nameとcategory)を見つけてください。重複レコードのみ(最初の出現以外)のproduct_id、product_name、category、priceを返してください。
[[ col ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.your_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ i18n.expected_results ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ error ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
[[ testData.solution ]]
利用可能なテーブル
[[ table.name ]]
| [[ col ]] |
|---|
| [[ formatCell(cell) ]] |
PARTITION BY句を使用したROW_NUMBER()関数の使用
SQLデータベースで重複を検索する場合、PARTITION BY句を使用したROW_NUMBER()関数が便利なツールです。この関数は、指定されたパーティション条件に基づいて、結果セット内の各行に一意の番号を割り当てます。
PARTITION BY句により、ユーザーは1つ以上の列に基づいて行をパーティションにグループ化できます。これは、ROW_NUMBER()関数が結果セット全体ではなく、各パーティション内の各行に一意の番号を割り当てることを意味します。
たとえば、注文ID、顧客ID、注文日の列を持つ顧客注文テーブルを考えてみましょう。各顧客の重複注文を見つけるには、次のクエリを使用できます:
SELECT order_id, customer_id, order_date,
ROW_NUMBER() OVER (PARTITION BY customer_id, order_date
ORDER BY order_id) AS row_num
FROM orders
このクエリでは、ROW_NUMBER()関数がPARTITION BY句と共に使用され、顧客と注文日で注文をグループ化します。その後、関数は注文IDに基づいて各パーティション内の各行に一意の番号を割り当てます。
結果のテーブルには、割り当てられた行番号を含む「row_num」という追加の列があります。その後、row_numが1より大きい行を選択することで重複を特定できます。
PARTITION BY句を使用したROW_NUMBER()関数は、SQLデータベースで重複を見つけるための強力なツールになり得ます。特定の条件に基づいて行をパーティションにグループ化することで、関数は各パーティション内の各行に一意の番号を割り当て、重複の特定を容易にします。
ROW_NUMBER()関数を使用したCASE文の使用
SQLでは、ROW_NUMBER()関数を使用して、結果セット内の各行に一意の連番を割り当てます。この関数はCASE文と組み合わせて使用して、テーブル内の重複レコードを特定できます。
CASE文により、結果セット内の各行に条件付きロジックを適用できます。CASE文内でROW_NUMBER()関数を利用することで、各レコードの出現回数を決定できます。
たとえば、次のSQLクエリを使用して、「first_name」と「last_name」列に基づいて「users」テーブル内の重複レコードを見つけることができます:
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY first_name, last_name ORDER BY id) > 1
THEN '重複'
ELSE '一意'
END AS duplicate_status
FROM users;
上記のクエリは、「first_name」と「last_name」列で結果セットをパーティション化し、「id」列で行を並べ替えます。ROW_NUMBER()関数は、各パーティション内の各行に一意の連番を割り当てます。
CASE文は、ROW_NUMBER()値が1より大きいかどうかをチェックします。大きい場合、その行は重複として識別されます。そうでない場合、行は一意として識別されます。
結果セットには、各行に「重複」または「一意」を表示する「duplicate_status」という追加の列が含まれます。
| id | first_name | last_name | duplicate_status | |
|---|---|---|---|---|
| 1 | John | Smith | john@example.com | 一意 |
| 2 | Jane | Doe | jane@example.com | 一意 |
| 3 | John | Smith | john.smith@example.com | 重複 |
| 4 | Bob | Johnson | bob@example.com | 一意 |
| 5 | Jane | Doe | jane.doe@example.com | 重複 |
上記の例では、行1、2、4は一意として識別され、行3と5は「first_name」と「last_name」列に基づいて重複として識別されます。
CASE文内でROW_NUMBER()関数を利用することで、SQLテーブル内の重複レコードを簡単に特定して管理できます。
Beekeeper Studioは無料でオープンソースのデータベースGUIです
今まで使った中で最高のSQLクエリ&エディタツールです。データベース管理に必要なすべてが揃っています。 - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studioは高速で直感的、使いやすいです。Beekeeperは多くのデータベースをサポートし、Windows、Mac、Linuxで快適に動作します。
Beekeeper Studioについてユーザーの声
"Beekeeper Studioは私の古いSQLワークフローを完全に置き換えました。高速で直感的で、データベース作業を再び楽しくしてくれます。"
"多くのデータベースGUIを試しましたが、Beekeeperは機能とシンプルさの完璧なバランスを実現しています。とにかく動きます。"