PostgreSQLでCOUNT(DISTINCT col)と別カラムの集約を同時に出す

  • PostgreSQL
  • SQL

この記事は、構成と文章の整理に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 employee
GROUP BY department;
department sum
HR 10000
IT 13000
Sales 5500

department を生の値として出力しているので、SQLの一般ルールとしてGROUP BYが必須になる。これはDISTINCTとは無関係で、集約関数と非集約カラムを同じSELECTに混ぜるときの通常の制約。

部門ごとにDISTINCTした合計を出したい場合はこう。

SELECT department, SUM(DISTINCT salary)
FROM employee
GROUP BY department;

HRは同じ5000が2回入っているので、DISTINCTで1回分だけ集計される。

複数カラムのDISTINCT

DISTINCTは1カラムしか使えないと思われがちだけど、複数カラムの組み合わせにも使える。

SELECT DISTINCT department, salary
FROM 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で動く