

PostgreSQLでCOUNT(DISTINCT col)と別カラムの集約を同時に出す
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
DISTINCTを使ったカラムと、別のカラムに対する集約関数を同時に出したい場面があった。GROUP BYが必要だと思い込んでたけど、条件次第では不要だった。
やりたかったこと
employee テーブルがあるとして、
| department | salary |
|---|---|
| HR | 5000 |
| HR | 5000 |
| IT | 7000 |
| IT | 6000 |
| Sales | 5500 |
departmentの重複しない件数(COUNT(DISTINCT department))salaryの合計(SUM(salary))
を1つのSELECTで出したかった。
GROUP BYなしで書ける
SELECTに並べる列が全部集約関数なら、GROUP BYは要らない。
SELECT COUNT(DISTINCT department), SUM(salary)FROM employee;これで1行、department の重複排除カウントと salary の合計が同時に取れる。DISTINCTしたカラムと別カラムの集約が競合するわけじゃなくて、単に別々の集約関数として並べているだけ。
GROUP BYが必要になるパターン
GROUP BYが必要になるのは、SELECTに集約されてない生のカラムを混ぜたとき。
SELECT department, SUM(salary)FROM employeeGROUP BY department;| department | sum |
|---|---|
| HR | 10000 |
| IT | 13000 |
| Sales | 5500 |
department を生の値として出力しているので、SQLの一般ルールとしてGROUP BYが必須になる。これはDISTINCTとは無関係で、集約関数と非集約カラムを同じSELECTに混ぜるときの通常の制約。
部門ごとにDISTINCTした合計を出したい場合はこう。
SELECT department, SUM(DISTINCT salary)FROM employeeGROUP BY department;HRは同じ5000が2回入っているので、DISTINCTで1回分だけ集計される。
複数カラムのDISTINCT
DISTINCTは1カラムしか使えないと思われがちだけど、複数カラムの組み合わせにも使える。
SELECT DISTINCT department, salaryFROM employee;これは department と salary の組み合わせで重複を排除する。
COUNT(DISTINCT ...) も複数引数を取れる。
SELECT COUNT(DISTINCT department, salary)FROM employee;department と salary の組み合わせのユニーク数をカウントする。PostgreSQLでは構文レベルで普通にサポートされてる。MySQLだと挙動が違うらしいので、そこは実機未確認。
まとめて言うと
- SELECT列が全部集約関数ならGROUP BY不要。DISTINCTしたカラムと別カラムの集約を1発で出せる
- 生カラムを混ぜるならGROUP BYが必要(これはDISTINCTの制約じゃなくSQL一般の制約)
- DISTINCTは複数カラムの組み合わせにも使えるし、COUNT(DISTINCT col1, col2)もPostgreSQLで動く