こんにちは、さるまりんです。
きょうのトピックは、PostgreSQLでの「集計クエリの書き方」の便利な機能、FILTERについてです。
ふだんSQLを書くとき、こんなふうに CASE WHEN
を多用していました。
SELECT
COUNT(*) AS total,
COUNT(CASE WHEN gender = 'male' THEN 1 END) AS male_count,
COUNT(CASE WHEN gender = 'female' THEN 1 END) AS female_count
FROM users;
もちろんこれでも動くし間違いではないんですけど。。
PostgreSQLなら、もっと読みやすくてスッキリ書ける方法があります。
FILTER句とは?
FILTER
句は、集計関数に対して個別のWHERE
条件をつけられる便利な機能です。
PostgreSQL 9.4以降で使えるこの構文、こんなふうに書きます:
COUNT(*) FILTER (WHERE 条件)
先ほどのクエリも、FILTER
を使うとこうなります:
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE gender = 'male') AS male_count,
COUNT(*) FILTER (WHERE gender = 'female') AS female_count
FROM users;
ね、見やすいし、タイポも減りそうでしょ?
FILTERを使った実用例いろいろ
ステータスごとの件数を一発で!
SELECT
COUNT(*) FILTER (WHERE status = 'todo') AS todo_count,
COUNT(*) FILTER (WHERE status = 'in_progress') AS in_progress_count,
COUNT(*) FILTER (WHERE status = 'done') AS done_count
FROM tasks;
CASE WHEN
を3回書くよりも、ずっとスマートでメンテもしやすいですね。
最近ログインしたユーザーの男女別集計
SELECT
COUNT(*) AS total_logged_in,
COUNT(*) FILTER (WHERE gender = 'male') AS male_logged_in,
COUNT(*) FILTER (WHERE gender = 'female') AS female_logged_in
FROM users
WHERE last_login >= CURRENT_DATE - INTERVAL '7 days';
期間で絞った上で、さらに集計関数の中で性別ごとに条件を分ける、という使い方です。
金額合計をステータス別に分けて
SELECT
SUM(amount) FILTER (WHERE status = 'paid') AS total_paid,
SUM(amount) FILTER (WHERE status = 'pending') AS total_pending,
SUM(amount) FILTER (WHERE status = 'cancelled') AS total_cancelled
FROM orders;
SUM(CASE WHEN ...)
でも書けますが、FILTER
句ならもっと読みやすくないですか?
FILTER
句はSQL標準に含まれているけれど、実際に対応しているのはPostgreSQLなど一部のRDBMSだけなので注意が必要です。PostgreSQLでもv9.4以降で対応しています。OracleやSQL Server、MySQLでは対応していないのでCASE WHEN
で書く必要があります。
FILTER
はCASE WHEN
より見やすくて(主観ですが)、COUNT
、SUM
、AVG
などとともに使えます。
PostgreSQLを使っているならうまく使うと良さそうですね。
読んでくださってありがとうございました。
それではまた!