PostgreSQL 入門

SQL DISTINCT キーワード

PostgreSQL(および標準 SQL)において、DISTINCT キーワードはデータ分析やレポート作成に欠かせない強力なツールです。これを使用することで、テーブルのカラムまたは複数カラムの組み合わせから一意の(ユニークな)値のみを識別して取得できます。大量の冗長な情報を含むデータセットを扱う際、現在どのようなカテゴリや項目が存在するのかを明確に把握するために非常に有用です。DISTINCT を効果的に使いこなすことは、効率的で正確な SQL クエリを記述するための重要なステップとなります。

1. DISTINCT 句の理解

DISTINCT 句は通常、SELECT ステートメントと組み合わせて使用され、結果セットから重複する行を削除します。これはテーブル内の一つまたは複数のカラムに適用できます。

基本構文は以下の通りです:

SELECT DISTINCT カラム1, カラム2, ...
FROM テーブル名;

DISTINCT が複数のカラムに適用される場合、それらのカラムの値の「ユニークな組み合わせ」のみを返します。また、DISTINCTNULL(空の値)も一つの独立した値として扱うことを覚えておいてください。つまり、あるカラムに複数の NULL が含まれている場合、結果セットには一つの NULL だけが返されます。

1.1 単一カラムの DISTINCT

まずはシンプルな例から始めましょう。以下のようなデータを持つ customers(顧客)テーブルがあると仮定します。

customer_id (顧客 ID)name (姓名)city (都市)
1John DoeNew York (ニューヨーク)
2Jane SmithLondon (ロンドン)
3Peter JonesNew York (ニューヨーク)
4Mary BrownParis (パリ)
5John DoeNew 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 DoeNew York (ニューヨーク)
Jane SmithLondon (ロンドン)
Peter JonesNew York (ニューヨーク)
Mary BrownParis (パリ)

"John Doe" と "New York" はそれぞれ単独では複数回出現していますが、2つのカラムを同時に DISTINCT で指定したため、システムはそれらを一つの組み合わせとして判断します。結果セットにおいて "John Doe" と "New York" の組み合わせは一つだけになります(顧客 ID 1 と 5 が全く同じ姓名と都市を持っていても、一つにまとめられます)。

1.3 DISTINCT と NULL 値

以下の products(製品)テーブルを例に挙げます。

product_id (製品 ID)product_name (製品名)category (カテゴリ)
1Laptop (ノートPC)Tech (テクノロジー)
2Mouse (マウス)Tech (テクノロジー)
3Keyboard (キーボード)Tech (テクノロジー)
4Monitor (モニター)NULL
5Speaker (スピーカー)NULL

次のクエリを実行すると:

SELECT DISTINCT category
FROM products;

結果は以下のようになります:

category (カテゴリ)
Tech (テクノロジー)
NULL

見ての通り、DISTINCTNULL を独立したユニークな値として扱います。欠損値(空の値)を含むデータを処理する際、この挙動を理解しておくことは非常に重要です。

2. 実務ケースとデモンストレーション

オンラインショップのデータベース管理を想定し、より実践的な DISTINCT の活用事例を見ていきましょう。

2.1 ケース 1:ユニークな製品カテゴリの検索

products テーブルに登録されている重複のない製品カテゴリをすべて特定したいとします。テーブル構造は以下の通りです。

product_idproduct_namecategoryprice
1LaptopElectronics (電子機器)1200
2MouseElectronics (電子機器)25
3KeyboardElectronics (電子機器)75
4T-ShirtApparel (アパレル)20
5JeansApparel (アパレル)60
6Coffee MakerHome Goods (家庭用品)50
7BlenderHome Goods (家庭用品)100
8ToasterHome Goods (家庭用品)40
9BookBooks (書籍)15
10E-BookBooks (書籍)10

ユニークなカテゴリを取得するクエリは次の通りです:

SELECT DISTINCT category
FROM products;

結果:

category
Electronics (電子機器)
Apparel (アパレル)
Home Goods (家庭用品)
Books (書籍)

2.2 ケース 2:配送先の市区町村と州・県のユニークな組み合わせ

以下のような構造を持つ orders(注文)テーブルを考えます。

order_idcustomer_idshipping_city (配送都市)shipping_state (配送州/県)order_date
1101New YorkNY2023-01-01
2102Los AngelesCA2023-01-02
3101New YorkNY2023-01-03
4103ChicagoIL2023-01-04
5102Los AngelesCA2023-01-05
6104HoustonTX2023-01-06
7105HoustonTX2023-01-07

配送先の都市と州のユニークな組み合わせを見つけるクエリ:

SELECT DISTINCT shipping_city, shipping_state
FROM orders;

結果:

shipping_cityshipping_state
New YorkNY
Los AngelesCA
ChicagoIL
HoustonTX

2.3 ケース 3:大文字・小文字の区別(ケースセンシティブ)への対応

PostgreSQL はデフォルトでケースセンシティブ(大文字・小文字を区別)です。英文データを扱う場合、これが DISTINCT の結果に影響を与えることがあります。例えば、以下の colors(色)テーブルを例にします。

color_idcolor_name
1Red
2red
3Green
4Blue

次のクエリを実行すると:

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