SQLのDISTINCT完全解説|重複排除・COUNT・GROUP BYとの違いと最適化
CONTENTS
「SQLで一覧を取得したら同じ値が何度も出てくる…」
「JOINしたら行数が想定より増えた…」
「ユニークな件数を正しく数えたい…」
こうした悩みを解決するのが DISTINCT です。
しかし実務では、
- DISTINCTを付けたのに結果が減らない
- COUNT(DISTINCT)で想定と違う件数になる
- パフォーマンスが急に悪化する
- JOINの重複を“隠しているだけ”になっている
といった問題も頻発します。
この記事では、
- DISTINCTの正しい仕組み
- 複数列での挙動
- COUNT(DISTINCT)の注意点
- GROUP BY・EXISTSとの違い
- パフォーマンス最適化
- 実務での設計判断
まで、現場視点で徹底解説します。
初心者にも分かりやすく、かつ実務経験者が納得できる深さで整理します。
DISTINCTの基礎知識
DISTINCTとは何か?
DISTINCTは、SELECT結果から重複行を除外する構文です。
重要なのは次の2点です。
- テーブルのデータは変更しない
- SELECTで指定した列の値で重複判定が行われる
つまり、表示結果だけを整えるための指定です。
基本構文
SELECT DISTINCT 列名 FROM テーブル名;
ポイント:
- DISTINCTはSELECTの直後に1回だけ書く
- 列ごとではなくSELECT全体にかかる
- 重複判定は「SELECTした列の組み合わせ」
サンプルで理解するDISTINCTの動き
DISTINCTなし
SELECT last_name FROM players;
データが4行あり、姓がすべて「Sato」なら:
Sato Sato Sato Sato
DISTINCTあり
SELECT DISTINCT last_name FROM players;
結果:
Sato
行数が4→1に減ります。
DISTINCTは「値を変える」のではなく、同じ行をまとめる動作をします。
複数列でDISTINCTを使う
SELECT DISTINCT col1, col2 FROM テーブル名;
重複判定は「col1だけ」ではありません。
col1とcol2の組み合わせ単位です。
よくある誤解:
col1だけユニークにしたい
これはできません。
その場合は、必要な列だけをSELECTします。
COUNT(DISTINCT ...) の正しい使い方
1列のユニーク件数
SELECT COUNT(DISTINCT last_name) FROM players;
NULLの扱い
- COUNT(DISTINCT)はNULLを数えない
- NULLを1種類として扱うなら
複数列のCOUNT(DISTINCT)
DBによって方言があります。
MySQLでは可能:
COUNT(DISTINCT col1, col2)
PostgreSQLでは以下形式:
COUNT(DISTINCT (col1, col2))
公式ドキュメント参照:
- MySQL公式:https://dev.mysql.com/doc/
- PostgreSQL公式:https://www.postgresql.org/docs/
移植性を重視するなら:
SELECT COUNT(*) FROM ( SELECT col1, col2 FROM table GROUP BY col1, col2 ) sub;
DISTINCTとGROUP BYの違い
| 目的 | DISTINCT | GROUP BY |
|---|---|---|
| 重複排除 | ◎ | ◎ |
| 集計 | × | ◎ |
| 拡張性 | △ | ◎ |
DISTINCTが向いている場面
- 選択肢リスト作成
- ユニーク一覧取得
- JOIN後の軽い整理
GROUP BYが向いている場面
- 部署ごとの人数
- 売上集計
- グループ単位の条件判断
実務で最も多いDISTINCTの誤用
① JOINの重複を隠しているだけ
SELECT DISTINCT customers.* FROM customers JOIN orders ...
本当に必要なのは:
SELECT * FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.id );
存在確認ならEXISTSが正解です。
② SELECT列が多すぎる
余計な列を含めると重複が消えません。
- 必要最小限の列に絞る
- 何をユニークにしたいか明確にする
NULLの扱いの本質
DISTINCTではNULLも「1種類」として扱われます。
例:
NULL NULL NULL
↓
NULL
NULLを除外するなら:
WHERE col IS NOT NULL
パフォーマンスへの影響
DISTINCTは内部的に:
- ソート
- ハッシュ集計
を行います。
以下の場合は遅くなりやすい:
- 大量データ
- 文字列が長い
- 対象列が多い
- WHEREで絞り込みが弱い
パフォーマンス改善の基本
-
WHEREで先に絞る
-
SELECT列を減らす
-
インデックスを適切に張る
-
重複が出ない設計にする
公式リファレンス:
- MySQL EXPLAIN:https://dev.mysql.com/doc/refman/en/explain.html
- PostgreSQL EXPLAIN:https://www.postgresql.org/docs/current/using-explain.html
DISTINCTの代替手法
GROUP BY
SELECT col FROM table GROUP BY col;
将来的に集計追加するならこちら。
EXISTS
JOIN+DISTINCTよりも明確。
ウィンドウ関数(高度)
PostgreSQLやMySQL8以降なら:
ROW_NUMBER() OVER (PARTITION BY col ORDER BY id)
重複の原因分析に有効。
実務設計で考えるべきこと
DISTINCTは「便利な後処理」です。
しかし本質的には:
- なぜ重複が発生しているか?
- データ設計は適切か?
- ユニーク制約は張るべきか?
ここまで考えるのが、エンジニアの仕事です。
まとめ
DISTINCTは:
- SELECT結果から重複を除外する構文
- 判定はSELECT列の組み合わせ単位
- COUNT(DISTINCT)で種類数取得可能
- GROUP BYやEXISTSと使い分ける
- パフォーマンスと設計意図に注意する
正しく使えば、SQLは読みやすく、正確で、高速になります。
次のステップ
もしこの記事を読んで、
- SQLの最適化をもっと極めたい
- データ設計から関わりたい
- パフォーマンス改善に挑戦したい
- 本質的なDB設計力を身につけたい
と感じたなら、あなたは既に“次の段階”にいます。
私たちは、
データ設計・DB最適化・アプリケーション設計まで一貫して考えるエンジニアチームです。
単なるCRUD開発ではなく、
- なぜそのクエリなのか
- なぜその設計なのか
- なぜその制約なのか
を議論しながら成長できる環境があります。
SQLやデータ設計に強いエンジニアとして本気で成長したい方は、ぜひ私たちの採用情報もご覧ください。
技術を深く理解したいあなたと、一緒に働ける日を楽しみにしています。
学んだSQLを、実務で使えるスキルにしたい方へ
本記事ではSQLの基本的な考え方や使い方を解説しましたが、
「実務で使えるレベルまで身につけたい」と感じた方も多いのではないでしょうか。
SQLは、文法を理解するだけでなく、
- どんなデータ構造で使われるのか
- どんなクエリが現場で求められるのか
を意識して学ぶことで、実践力が大きく変わります。
そうした「実務を見据えたSQL学習」を進めたい方には、
完全無料で学べるプログラミングスクール ZeroCode という選択肢もあります。
- SQLを含むWeb・データベース系スキルを体系的に学べる
- 未経験者でも実務を意識したカリキュラム
- 受講料・教材費がかからない完全無料の学習環境
- 完全オンラインでスキマ学習
※学習内容や進め方を確認するだけでもOKです