17
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

仕事でPostgreSQLのDBに保存してあるデータの統計を取っていたときに、便利なSQLに出会ったので紹介しようと思います。

準備

以下のSQL文でセットアップします。

create table items
(
    id                 integer not null primary key,
    name               varchar(50) not null,
    category           varchar(50),
    stock              integer
);

insert into items (id, name, category, stock)
values (1, 'りんご', 'フルーツ', 10),
       (2, 'みかん', 'フルーツ', 20),
       (3, 'なし', 'フルーツ', 30),
       (4, 'レタス', '野菜', 40),
       (5, 'トマト', '野菜', 50),
       (6, 'キャベツ', '野菜', 60);

集約関数

よく目にする集約関数といえば以下のような使い方だと思います。

  • カテゴリーごとの在庫総数を求めたい
select category, sum ( stock )
from items
group by category;
  • カテゴリーごとの商品の種類を求めたい
select category, count ( * )
from items
group by category;

本記事で紹介する小技

本記事では、「filter関数」を紹介したいと思います。

filter関数の例

カテゴリー「フルーツ」に占める各フルーツの割合を出す

以下のSQLにより算出が可能です。

select
    category as "カテゴリー",
    sum ( stock ) as "カテゴリーの総数",
    sum ( stock ) filter ( where name = 'りんご' ) as "りんごの総数",
    round ( sum ( stock ) filter ( where name = 'りんご' )::decimal  / sum ( stock ) * 100, 2 ) as "りんごの割合",
    sum ( stock ) filter ( where name = 'みかん' ) as "みかんの総数",
    round ( sum ( stock ) filter ( where name = 'みかん' )::decimal  / sum ( stock ) * 100, 2 ) as "みかんの割合",
    sum ( stock ) filter ( where name = 'なし' ) as "なしの総数",
    round ( sum ( stock ) filter ( where name = 'なし' )::decimal  / sum ( stock ) * 100, 2 ) as "なしの割合"
from items
where category = 'フルーツ'
group by category;

結果は以下のとおり。

カテゴリー カテゴリーの総数 りんごの総数 りんごの割合 みかんの総数 みかんの割合 なしの総数 なしの割合
フルーツ 60 10 16.67% 20 33.33% 30 50%

集約関数と一緒に利用できる「filter関数」、統計を取るまで知らなかったのですが実は便利な関数でした。

補足

カテゴリー「フルーツ」に占める各フルーツの割合を出す
というお題の場合

select
    category as "カテゴリー",
    sum ( stock ) as "カテゴリーの総数",
    sum ( case name when 'りんご' then stock else 0 end ) as "りんごの総数",
    round ( sum ( case name when 'りんご' then stock else 0 end )::decimal / sum(stock) * 100, 2 ) as "りんごの割合",
    sum ( case name when 'みかん' then stock else 0 end) as "みかんの総数",
    round ( sum ( case name when 'みかん' then stock else 0 end )::decimal / sum(stock) * 100, 2 ) as "みかんの割合",
    sum (case name when 'なし' then stock else 0 end) as "なしの総数",
    round ( sum(case name when 'りんご' then stock else 0 end )::decimal / sum(stock) * 100, 2 ) as "りんごの割合"
from items
where category = 'フルーツ'
group by category;

という書き方も問題なく動作します。

結論

集約関数の中にcase-whenを書くことも可能ですが、SQLが冗長になります。
filter関数を利用すると、通常のSQLと同じ条件式の書き方ができるので他のSQLの流用ができます。

まとめると以下のようになります。

種類 可読性の面 冗長の面 複数条件
case-whenを使う Bad Bad Bad
filter関数を使う Good Good Good
17
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?