SQLのDISTINCT句:重複を排除してユニークな値を取得する方法
SQLを扱っていると、「重複したデータを除外して、ユニークな値だけを取得したい」という場面に頻繁に遭遇します。例えば、顧客リストから重複するメールアドレスを省いてユニークなリストを作成したい場合や、商品リストから重複するカテゴリ名を抽出したい場合などです。
このようなニーズに応えるのが、SQLのDISTINCT句です。
しかし、「DISTINCTって具体的にどう使うの?」「SELECT文のどこに書けばいいの?」といった疑問を持つ方もいるかもしれません。
この記事では、DISTINCT句の基本的な使い方から、少し応用的な使い方までを、具体的なSQLコード例を交えながら分かりやすく解説していきます。
DISTINCTを使いこなせるようになれば、より効率的かつ正確にデータを分析できるようになりますので、ぜひ最後までご覧ください。
SELECT文におけるDISTINCTの基本
DISTINCT句は、SELECT文で取得する結果セットから、指定した列の重複する値を取り除き、ユニークな値のみを返します。
最も基本的な使い方は、SELECTキーワードの直後にDISTINCTを記述することです。
例えば、usersテーブルからユニークなcity(都市名)のリストを取得したい場合、以下のようなSQL文になります。
SELECT DISTINCT city
FROM users;
このクエリは、usersテーブルに登録されているすべての都市名を検索し、その中から重複する都市名を削除して、ユニークな都市名のみを一覧表示します。
もしusersテーブルに以下のようなデータがあったとします。
| id | name | city |
|---|---|---|
| 1 | Alice | Tokyo |
| 2 | Bob | Osaka |
| 3 | Charlie | Tokyo |
| 4 | David | Nagoya |
| 5 | Eve | Osaka |
上記のSELECT DISTINCT city FROM users;を実行すると、結果は以下のようになります。
| city |
|---|
| Tokyo |
| Osaka |
| Nagoya |
TokyoやOsakaのように複数回出現する都市名も、DISTINCTによって1つにまとめられていることがわかります。
複数の列にDISTINCTを適用する場合
DISTINCT句は、単一の列だけでなく、複数の列に対して適用することも可能です。
複数の列にDISTINCTを適用した場合、指定したすべての列の組み合わせがユニークであるレコードのみが返されます。
例えば、ordersテーブルから、ユニークなcustomer_id(顧客ID)とorder_date(注文日)の組み合わせを取得したいとします。
SELECT DISTINCT customer_id, order_date
FROM orders;
この場合、customer_idとorder_dateの両方が同じであるレコードは、重複とみなされ、一つだけが結果に含まれます。
customer_idが同じでもorder_dateが異なれば、それはユニークな組み合わせとして扱われます。
例えば、ordersテーブルに以下のようなデータがあったとします。
| order_id | customer_id | order_date | amount |
|---|---|---|---|
| 101 | 1 | 2023-10-26 | 5000 |
| 102 | 2 | 2023-10-26 | 3000 |
| 103 | 1 | 2023-10-27 | 7000 |
| 104 | 1 | 2023-10-26 | 2000 |
この場合、SELECT DISTINCT customer_id, order_date FROM orders;を実行すると、結果は以下のようになります。
| customer_id | order_date |
|---|---|
| 1 | 2023-10-26 |
| 2 | 2023-10-26 |
| 1 | 2023-10-27 |
customer_idが1でorder_dateが2023-10-26のレコードは、order_idが異なっていても、customer_idとorder_dateの組み合わせとしては重複しているとみなされます。そのため、order_idが101と104の2つのレコードのうち、どちらか一方が結果に含まれます(どのレコードが残るかはデータベースの実装によりますが、一般的にはいずれか一つです)。
customer_idが1でorder_dateが2023-10-27のレコードは、order_dateが異なるため、ユニークな組み合わせとして扱われます。
DISTINCTを複数の列に適用する際は、各列が独立してユニークになるのではなく、「列の組み合わせ」としてユニークになる点を理解しておくことが重要です。
GROUP BY句との違い
DISTINCT句と似たような目的で使われるものにGROUP BY句があります。
どちらも重複するデータをまとめる機能がありますが、その役割と使い方には明確な違いがあります。
GROUP BY句は、指定した列の値に基づいて行をグループ化し、各グループに対して集計関数(COUNT, SUM, AVG, MAX, MINなど)を適用するために使用されます。
一方、DISTINCT句は、単純に重複する行を削除し、ユニークな行のみを返します。集計関数と組み合わせて使うことも可能ですが、その場合はGROUP BY句を使った場合と結果が異なることがあります。
例えば、usersテーブルから各都市のユーザー数をカウントしたい場合を考えます。
GROUP BY句を使う場合:
SELECT city, COUNT(*) AS user_count
FROM users
GROUP BY city;
このクエリは、cityごとにグループ化し、各グループの行数をカウントします。結果は以下のようになります。
| city | user_count |
|---|---|
| Tokyo | 2 |
| Osaka | 2 |
| Nagoya | 1 |
DISTINCT句をCOUNT関数と組み合わせて使う場合:
SELECT COUNT(DISTINCT city) AS unique_city_count
FROM users;
このクエリは、usersテーブルに含まれるユニークなcityの数をカウントします。結果は以下のようになります。
| unique_city_count | |-------------------|--------| | 3 |
このように、COUNT(DISTINCT column_name)は、指定した列のユニークな値の数を数えるために使われます。
GROUP BYは「グループごとに集計したい」、COUNT(DISTINCT ...)は「ユニークな値の数を数えたい」という目的で使い分けることが重要です。
また、SELECT DISTINCT column1, column2 FROM table_name;は、GROUP BY column1, column2と結果が同じになる場合がありますが、これはGROUP BYの副次的な効果であり、DISTINCTの本来の目的は「重複行の削除」にあります。
パフォーマンスの観点からも、意図した結果を得るためには、それぞれの句の役割を正確に理解し、適切に使い分けることが推奨されます。
DISTINCTION句の応用と注意点
DISTINCT句は、データのクリーニングや集計の前処理として非常に役立ちます。しかし、いくつか注意すべき点もあります。
NULL値の扱い
DISTINCT句は、NULL値を他の値とは異なる特別な値として扱います。つまり、NULL値もユニークな値としてカウントされます。
例えば、productsテーブルに以下のようなデータがあったとします。
| product_id | category |
|---|---|
| 1 | Books |
| 2 | NULL |
| 3 | Books |
| 4 | NULL |
| 5 | Clothing |
SELECT DISTINCT category FROM products;を実行すると、結果は以下のようになります。
| category | |----------|----------| | Books | | NULL | | Clothing |
NULLが1つだけ結果に含まれていることがわかります。もしNULLを結果に含めたくない場合は、WHERE句で除外する必要があります。
SELECT DISTINCT category
FROM products
WHERE category IS NOT NULL;
このクエリでは、categoryがNULLでないレコードのみを対象とするため、結果からNULLが除外されます。
パフォーマンスへの影響
DISTINCT句を使用すると、データベースは重複する値を見つけるために、結果セット全体をソートまたはハッシュ処理する必要があります。データ量が多い場合、この処理には時間がかかることがあります。
特に、複数の列にDISTINCTを適用した場合や、ORDER BY句と併用した場合、パフォーマンスに影響が出やすくなります。
もし、単にユニークな値の数を数えたいだけであれば、COUNT(DISTINCT column_name)を使用する方が、SELECT DISTINCT column_nameしてからCOUNT(*)するよりも効率的な場合が多いです。
パフォーマンスが問題になる場合は、インデックスの活用や、GROUP BY句での代替などを検討することも重要です。
LIMIT句との併用
DISTINCT句とLIMIT句を併用すると、ユニークな結果の中からさらに上位N件を取得することができます。
例えば、usersテーブルからユニークな都市名を5つ取得したい場合:
SELECT DISTINCT city
FROM users
LIMIT 5;
ただし、LIMIT句は結果セットの順序に依存するため、ORDER BY句を指定せずにLIMITを使用すると、どの5件が取得されるかは保証されません。
もし特定の順序で上位5件のユニークな都市名を取得したい場合は、ORDER BY句を併用する必要があります。
SELECT DISTINCT city
FROM users
ORDER BY city
LIMIT 5;
このクエリは、まず都市名をアルファベット順に並べ替え、その中から最初の5つのユニークな都市名を取得します。
DISTINCT句は、データの重複を取り除き、分析に必要なユニークな情報を効率的に抽出するための強力なツールです。その基本的な使い方から応用、注意点までを理解することで、SQLでのデータ操作の幅が大きく広がります。
今回学習したDISTINCT句は、データ分析の基本となる「重複排除」の操作です。この知識をさらに深めるために、ぜひ以下の練習問題に挑戦してみてください。