COUNT(DISTINCT column) は、列に含まれる異なる値の数を数えます。10回出現する値も1つとして数え、NULL はまったく数えません。例は PostgreSQL、MySQL、SQLite、SQL Server で動作します。
COUNT DISTINCT の使い方
COUNT() の中で、数えたい列や式の前に DISTINCT を置きます。
COUNT(DISTINCT expression)
他の2つの COUNT の形式と並べると分かりやすいです。COUNT(*) は行数を、COUNT(column) は列が NULL でない行数を、COUNT(DISTINCT column) は NULL 以外の異なる値の数を数えます。
-- Input: orders
-- order_id | customer_id
-- ---------+------------
-- 1 | 1042
-- 2 | 1077
-- 3 | 1042
-- 4 | NULL
SELECT COUNT(*) AS all_rows,
COUNT(customer_id) AS with_customer,
COUNT(DISTINCT customer_id) AS customers
FROM orders;
-- Output:
-- all_rows | with_customer | customers
-- ---------+---------------+----------
-- 4 | 3 | 2
4件の注文は2人の顧客からのものです。1042 は2回注文しており、注文 4 には顧客がいません。
知っておくべきルール
- NULL は数えられません。欠損値も1つとして数えたい場合は、自分で加算します。
-- Input: the orders table above
SELECT COUNT(DISTINCT customer_id)
+ MAX(CASE WHEN customer_id IS NULL THEN 1 ELSE 0 END) AS customers_and_guests
FROM orders;
-- Output: 3
-
MySQL と SQL Server はデフォルトで大文字・小文字を区別しないため、
'SQL'と'sql'を1つの値として数えます。大文字・小文字を区別したくない場合はLOWER(tag)を数えます。
-- Input: tags
-- post_id | tag
-- --------+---------
-- 1 | SQL
-- 2 | sql
-- 3 | Postgres
SELECT COUNT(DISTINCT tag) FROM tags;
-- Output: 3 (2 in MySQL and SQL Server)
SELECT COUNT(DISTINCT LOWER(tag)) FROM tags;
-- Output: 2
-
DISTINCT は
SUMやAVGの中でも使えますが、取り除かれるのは重複した行ではなく重複した値です。同じ価格の異なる2つの商品は1回しか加算されません。
-- Input: order_lines
-- product | price
-- ---------+------
-- Pen | 2.00
-- Pencil | 2.00
-- Notebook | 5.00
SELECT SUM(price) AS total, SUM(DISTINCT price) AS distinct_total FROM order_lines;
-- Output:
-- total | distinct_total
-- ------+---------------
-- 9.00 | 7.00
別の構文
COUNT(DISTINCT ...) は1つの列しか受け取りません。複数列の異なる組み合わせを数えるには、DISTINCT サブクエリの行数を数えます。PostgreSQL と MySQL には省略記法もあります。
-- Input: orders
-- order_id | customer_id | product_id
-- ---------+-------------+-----------
-- 1 | 1 | 23
-- 2 | 12 | 3
-- 3 | 1 | 23
-- 4 | 12 | 5
SELECT COUNT(*) FROM (SELECT DISTINCT customer_id, product_id FROM orders) AS pairs;
-- Output: 3
-- PostgreSQL
SELECT COUNT(DISTINCT (customer_id, product_id)) FROM orders;
-- Output: 3
-- MySQL
SELECT COUNT(DISTINCT customer_id, product_id) FROM orders;
-- Output: 3
CONCAT() で列を1つの文字列に連結する方法も見かけます。この場合は区切り文字が必要です。区切り文字がないと、顧客 1 と商品 23、顧客 12 と商品 3 がどちらも '123' になってしまいます。
-- Input: the orders table above
SELECT COUNT(DISTINCT CONCAT(customer_id, product_id)) FROM orders;
-- Output: 2
SELECT COUNT(DISTINCT CONCAT(customer_id, '_', product_id)) FROM orders;
-- Output: 3
正確な DISTINCT カウントはすべての値をソートまたはハッシュする必要があるため、非常に大きなテーブルでは遅くなります。SQL Server には APPROX_COUNT_DISTINCT() があり、より高速で、97% の確率で正確な値との誤差が 2% 以内に収まります。
-- SQL Server
-- Input: the orders table above
SELECT APPROX_COUNT_DISTINCT(customer_id) AS customers FROM orders;
-- Output: 2
COUNT DISTINCT はいつ使うべきか?
「何行あるか?」ではなく「異なる…はいくつあるか?」を知りたいときに COUNT DISTINCT を使います。そうした問いのほとんどは、次の4つのいずれかに当てはまります。
ユニークな訪問者・顧客・商品を数える
訪問・注文・売上のログには、同じ訪問者、顧客、商品が何度も記録されます。その ID に COUNT DISTINCT を使えば、行数を実際の人数や件数に変換できます。
グループごとにユニークな値を数える
GROUP BY を加えると、グループごとに DISTINCT カウントが得られます。コースごとの受講者数、ベンダーごとの商品数、月ごとの顧客数などです。
列のユニークな組み合わせを数える
2つの列が両方一致するときだけ2行が「同じ」とみなされる場合があります。同じユーザーの同じ種類の問題や、2つの空港間の同じ路線などです。「別の構文」で紹介した複数列の形式のいずれかで組み合わせを数えます。
条件を満たすユニークな値を数える
COUNT(DISTINCT ...) の中に CASE 式を入れると、一部の行だけを数えられます。CASE は不要な行に対して NULL を返し、NULL は数えられないため、1つのクエリで複数の絞り込みカウントを並べて返せます。
COUNT DISTINCT の実例
月間アクティブユーザー
9月に何らかの操作をしたユーザーを、何回戻ってきたかに関係なく数えます。
-- Input: events
-- event_id | user_id | event_date
-- ---------+---------+-----------
-- 1 | 301 | 2026-08-30
-- 2 | 302 | 2026-09-02
-- 3 | 302 | 2026-09-15
-- 4 | 305 | 2026-09-21
-- 5 | 302 | 2026-10-01
SELECT COUNT(DISTINCT user_id) AS active_users
FROM events
WHERE event_date >= '2026-09-01' AND event_date < '2026-10-01';
-- Output: 2
ベンダーごとの販売商品
COUNT(*) は各ベンダーの販売件数を、COUNT(DISTINCT product) はそれらの販売に含まれる異なる商品の数を返します。
-- Input: sales
-- vendor | product | color
-- -----------+----------+------
-- Acme | Mug | Red
-- Acme | Mug | Blue
-- Acme | Mug | Red
-- Acme | Tote Bag | Red
-- Brightside | Notebook | Black
SELECT vendor, COUNT(*) AS sales, COUNT(DISTINCT product) AS products
FROM sales
GROUP BY vendor;
-- Output:
-- vendor | sales | products
-- -----------+-------+---------
-- Acme | 4 | 2
-- Brightside | 1 | 1
航空会社の運航路線
路線は出発地と目的地の組み合わせです。LHR から JFK への2便は1路線ですが、JFK から LHR は別の路線です。
-- Input: flights
-- flight_no | origin | destination
-- ----------+--------+------------
-- 101 | LHR | JFK
-- 105 | LHR | JFK
-- 102 | JFK | LHR
-- 301 | LHR | LAX
SELECT COUNT(*) AS routes
FROM (SELECT DISTINCT origin, destination FROM flights) AS r;
-- Output: 3
購入価格帯ごとの顧客
商品 100〜200 を購入した顧客を数え、注文の価格帯で分けます。顧客 7 は安価な注文を2回していますが1人として数え、顧客 15 の商品は範囲外です。
-- Input: orders
-- order_id | customer_id | product_id | price
-- ---------+-------------+------------+-------
-- 1 | 7 | 105 | 25.00
-- 2 | 7 | 142 | 40.00
-- 3 | 9 | 150 | 120.00
-- 4 | 12 | 188 | 310.00
-- 5 | 15 | 260 | 30.00
SELECT
COUNT(DISTINCT customer_id) AS customers,
COUNT(DISTINCT CASE WHEN price BETWEEN 1 AND 50 THEN customer_id END) AS low_spenders,
COUNT(DISTINCT CASE WHEN price BETWEEN 51 AND 150 THEN customer_id END) AS mid_spenders,
COUNT(DISTINCT CASE WHEN price > 150 THEN customer_id END) AS high_spenders
FROM orders
WHERE product_id BETWEEN 100 AND 200;
-- Output:
-- customers | low_spenders | mid_spenders | high_spenders
-- ----------+--------------+--------------+--------------
-- 3 | 1 | 1 | 1
2つの価格帯で注文した顧客は、両方で数えられます。
COUNT DISTINCT の関連資料
このブログの記事:
-
SQL GROUP BY ガイド: グループごとの
COUNT、SUM、AVG - SQL 重複検索ガイド: 複数回出現する値を見つける
- SQL 重複削除ガイド: 余分なコピーを削除する
- SQL CASE 文ガイド: 条件付きカウントの条件部分
-
SQL の Having と Where の違い:
HAVINGでカウントを絞り込む - SQL チートシート: 集計関数をはじめ SQL 全体を1ページに
公式ドキュメント:
Beekeeper Studioは無料のオープンソースのデータベースGUIです
これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。
Beekeeper Studio 利用者の声
「Beekeeper Studio は以前の SQL ワークフローを完全に置き換えました。高速で直感的、データベース作業がまた楽しくなりました。」
「多くのデータベース GUI を試しましたが、Beekeeper は機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」