コードロード

エラー討伐

【SQL】CASE式で条件分岐を表現

達人に学ぶSQL徹底指南所の学習記録です。

やってること

ラベルの読み替えを行なっているだけ。

要点

  • CASEは「式」だから、列名や定数をかける場所には常にかける
  • 集約関数の中で使うことで、行持ちから列持ちへの水平展開が可能
  • 逆に、集約関数を条件式に組み込むと HAVING を使わずにクエリをまとめられる
  • CASEを使うと複数のSQL分を1つにまとめられて、可読性もパフォーマンスも上がる

注意

  • END の書き忘れに注意!
  • ELSE は必ず書こう!
    • エラーにはならないけど結果が違うというバグの温床になるため、たとえNULLでも明示的に書く癖をつけよう

問題1

mysql> select * from Greatests;
+-----+---+---+---+
| id  | x | y | z |
+-----+---+---+---+
| A   | 1 | 2 | 3 |
| B   | 5 | 5 | 2 |
| C   | 4 | 7 | 1 |
| D   | 3 | 3 | 8 |
+-----+---+---+---+

xとyの最大値を取得する。

mysql> SELECT id, 
    -> CASE WHEN x < y THEN y ELSE x END AS greates
    -> FROM Greatests;
+----+---------+
| id | greates |
+----+---------+
| A  |       2 |
| B  |       5 |
| C  |       7 |
| D  |       3 |
+----+---------+

xyzの最大値を取得する。

mysql> SELECT id, 
    -> CASE WHEN CASE WHEN x < y THEN y ELSE x END < z THEN z 
    -> ELSE CASE WHEN x < y THEN y ELSE x END END AS greatest
    -> FROM Greatests;
+----+----------+
| id | greatest |
+----+----------+
| A  |        3 |
| B  |        5 |
| C  |        7 |
| D  |        8 |
+----+----------+

問題2

mysql> SELECT * FROM  pref_samples;
+-----------+-----+------------+
| pref_name | sex | population |
+-----------+-----+------------+
| 徳島      |   1 |         60 |
| 徳島      |   2 |         40 |
| 香川      |   1 |        100 |
| 香川      |   2 |        100 |
| 愛媛      |   1 |        100 |
| 愛媛      |   2 |         50 |
| 高知      |   1 |        100 |
| 高知      |   2 |        100 |
| 福岡      |   1 |        100 |
| 福岡      |   2 |        200 |
| 佐賀      |   1 |         20 |
| 佐賀      |   2 |         80 |
| 長崎      |   1 |        125 |
| 長崎      |   2 |        125 |
| 東京      |   1 |        250 |
| 東京      |   2 |        150 |
+-----------+-----+------------+

性別ごとに、全国、各県、四国の人口を取得

mysql> SELECT sex, 
    -> SUM(population) AS total,
    -> SUM(CASE WHEN pref_name = '徳島' THEN population ELSE 0 END) AS 徳島,
    -> SUM(CASE WHEN pref_name = '香川' THEN population ELSE 0 END) AS 香川,
    -> SUM(CASE WHEN pref_name = '愛媛' THEN population ELSE 0 END) AS 愛媛,
    -> SUM(CASE WHEN pref_name = '高知' THEN population ELSE 0 END) AS 高知,
    -> SUM(CASE WHEN pref_name IN ('徳島', '香川', '愛媛', '高知') THEN population ELSE 0 END) AS 四国
    -> FROM pref_samples
    -> GROUP BY sex;
+-----+-------+--------+--------+--------+--------+--------+
| sex | total | 徳島   | 香川   | 愛媛   | 高知   | 四国   |
+-----+-------+--------+--------+--------+--------+--------+
|   1 |   855 |     60 |    100 |    100 |    100 |    360 |
|   2 |   845 |     40 |    100 |     50 |    100 |    290 |
+-----+-------+--------+--------+--------+--------+--------+