SQL DISTINCT キーワード
PostgreSQL(および標準 SQL)において、DISTINCT キーワードはデータ分析やレポート作成に欠かせない強力なツールです。これを使用することで、テーブルのカラムまたは複数カラムの組み合わせから一意の(ユニークな)値のみを識別して取得できます。大量の冗長な情報を含むデータセットを扱う際、現在どのようなカテゴリや項目が存在するのかを明確に把握するために非常に有用です。DISTINCT を効果的に使いこなすことは、効率的で正確な SQL クエリを記述するための重要なステップとなります。
1. DISTINCT 句の理解
DISTINCT 句は通常、SELECT ステートメントと組み合わせて使用され、結果セットから重複する行を削除します。これはテーブル内の一つまたは複数のカラムに適用できます。
基本構文は以下の通りです:
SELECT DISTINCT カラム1, カラム2, ...
FROM テーブル名;DISTINCT が複数のカラムに適用される場合、それらのカラムの値の「ユニークな組み合わせ」のみを返します。また、DISTINCT は NULL(空の値)も一つの独立した値として扱うことを覚えておいてください。つまり、あるカラムに複数の NULL が含まれている場合、結果セットには一つの NULL だけが返されます。
1.1 単一カラムの DISTINCT
まずはシンプルな例から始めましょう。以下のようなデータを持つ customers(顧客)テーブルがあると仮定します。
| customer_id (顧客 ID) | name (姓名) | city (都市) |
|---|---|---|
| 1 | John Doe | New York (ニューヨーク) |
| 2 | Jane Smith | London (ロンドン) |
| 3 | Peter Jones | New York (ニューヨーク) |
| 4 | Mary Brown | Paris (パリ) |
| 5 | John Doe | New York (ニューヨーク) |
重複のない都市リストを取得するには、次のクエリを使用します:
SELECT DISTINCT city
FROM customers;このクエリの実行結果は以下の通りです:
| city (都市) |
|---|
| New York (ニューヨーク) |
| London (ロンドン) |
| Paris (パリ) |
元のテーブルには "New York" が3回出現していましたが、結果セットには1回だけ表示されている点に注目してください。
1.2 多列(マルチカラム)の DISTINCT
次に、「姓名」と「都市」のユニークな組み合わせを検索したい場合を考えます。同じ customers テーブルを使用して、次のクエリを実行します:
SELECT DISTINCT name, city
FROM customers;このクエリの実行結果は以下の通りです:
| name (姓名) | city (都市) |
|---|---|
| John Doe | New York (ニューヨーク) |
| Jane Smith | London (ロンドン) |
| Peter Jones | New York (ニューヨーク) |
| Mary Brown | Paris (パリ) |
"John Doe" と "New York" はそれぞれ単独では複数回出現していますが、2つのカラムを同時に DISTINCT で指定したため、システムはそれらを一つの組み合わせとして判断します。結果セットにおいて "John Doe" と "New York" の組み合わせは一つだけになります(顧客 ID 1 と 5 が全く同じ姓名と都市を持っていても、一つにまとめられます)。
1.3 DISTINCT と NULL 値
以下の products(製品)テーブルを例に挙げます。
| product_id (製品 ID) | product_name (製品名) | category (カテゴリ) |
|---|---|---|
| 1 | Laptop (ノートPC) | Tech (テクノロジー) |
| 2 | Mouse (マウス) | Tech (テクノロジー) |
| 3 | Keyboard (キーボード) | Tech (テクノロジー) |
| 4 | Monitor (モニター) | NULL |
| 5 | Speaker (スピーカー) | NULL |
次のクエリを実行すると:
SELECT DISTINCT category
FROM products;結果は以下のようになります:
| category (カテゴリ) |
|---|
| Tech (テクノロジー) |
| NULL |
見ての通り、DISTINCT は NULL を独立したユニークな値として扱います。欠損値(空の値)を含むデータを処理する際、この挙動を理解しておくことは非常に重要です。
2. 実務ケースとデモンストレーション
オンラインショップのデータベース管理を想定し、より実践的な DISTINCT の活用事例を見ていきましょう。
2.1 ケース 1:ユニークな製品カテゴリの検索
products テーブルに登録されている重複のない製品カテゴリをすべて特定したいとします。テーブル構造は以下の通りです。
| product_id | product_name | category | price |
|---|---|---|---|
| 1 | Laptop | Electronics (電子機器) | 1200 |
| 2 | Mouse | Electronics (電子機器) | 25 |
| 3 | Keyboard | Electronics (電子機器) | 75 |
| 4 | T-Shirt | Apparel (アパレル) | 20 |
| 5 | Jeans | Apparel (アパレル) | 60 |
| 6 | Coffee Maker | Home Goods (家庭用品) | 50 |
| 7 | Blender | Home Goods (家庭用品) | 100 |
| 8 | Toaster | Home Goods (家庭用品) | 40 |
| 9 | Book | Books (書籍) | 15 |
| 10 | E-Book | Books (書籍) | 10 |
ユニークなカテゴリを取得するクエリは次の通りです:
SELECT DISTINCT category
FROM products;結果:
| category |
|---|
| Electronics (電子機器) |
| Apparel (アパレル) |
| Home Goods (家庭用品) |
| Books (書籍) |
2.2 ケース 2:配送先の市区町村と州・県のユニークな組み合わせ
以下のような構造を持つ orders(注文)テーブルを考えます。
| order_id | customer_id | shipping_city (配送都市) | shipping_state (配送州/県) | order_date |
|---|---|---|---|---|
| 1 | 101 | New York | NY | 2023-01-01 |
| 2 | 102 | Los Angeles | CA | 2023-01-02 |
| 3 | 101 | New York | NY | 2023-01-03 |
| 4 | 103 | Chicago | IL | 2023-01-04 |
| 5 | 102 | Los Angeles | CA | 2023-01-05 |
| 6 | 104 | Houston | TX | 2023-01-06 |
| 7 | 105 | Houston | TX | 2023-01-07 |
配送先の都市と州のユニークな組み合わせを見つけるクエリ:
SELECT DISTINCT shipping_city, shipping_state
FROM orders;結果:
| shipping_city | shipping_state |
|---|---|
| New York | NY |
| Los Angeles | CA |
| Chicago | IL |
| Houston | TX |
2.3 ケース 3:大文字・小文字の区別(ケースセンシティブ)への対応
PostgreSQL はデフォルトでケースセンシティブ(大文字・小文字を区別)です。英文データを扱う場合、これが DISTINCT の結果に影響を与えることがあります。例えば、以下の colors(色)テーブルを例にします。
| color_id | color_name |
|---|---|
| 1 | Red |
| 2 | red |
| 3 | Green |
| 4 | Blue |
次のクエリを実行すると:
SELECT DISTINCT color_name
FROM colors;結果には、大文字・小文字が異なる重複した項目が含まれてしまいます:
| color_name |
|---|
| Red |
| red |
| Green |
| Blue |
先頭が大文字の "Red" とすべて小文字の "red" を同じ値として扱いたい場合は、LOWER() 関数を使用して、DISTINCT を適用する前にすべての値を小文字に統一します。
SELECT DISTINCT LOWER(color_name)
FROM colors;これにより、完全に重複が排除された結果が得られます:
| lower |
|---|
| red |
| green |
| blue |