コマンド道場

CASE 式の使い方

元の値から、新しい列を作る

shain社員

namesalary
鈴木980000
佐藤720000
田中520000
中村530000
小林445050
加藤610000

CASE で作った列を足す分類した結果

namesalarykaikyu
鈴木980000A
佐藤720000A
田中520000B
中村530000B
小林445050C
加藤610000B

CASE WHEN salary >= 700000 THEN 'A' WHEN salary >= 500000 THEN 'B' ELSE 'C' END AS kaikyu

  • 青い列= 式から作った列。元の表には無い列です
  • 社員 18分類した結果 18(行数は変わりません。減らすのは WHERE の仕事です)

行は 1 行も減っていません。CASE は「行を選ぶ」道具ではなく「値を作る」道具です。全 18 行では A が 3 人・B が 7 人・C が 8 人になります。

CASE は「値によって別の値を返す」式です。 行を絞るのではなく、元の値から新しい列を作ります。いちばん事故が多いのは 条件が上から順に評価されることで、順序を間違えると一生届かない枝ができます。

主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2

製品ごとの対応表(根拠つき)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01書き方は 2 つある

結論: 条件を書く形(検索形)と、値を並べる形(簡単形)の 2 つです。

  • 検索形CASE WHEN 条件 THEN 値 … ELSE 値 END
  • 簡単形CASE 列 WHEN 値 THEN 値 … ELSE 値 END

簡単形は「その列が○○と等しいなら」という等値比較しかできません。範囲で分けたいときは検索形を使います。

迷ったら検索形にしてください。 書ける条件が広く、あとから範囲条件を足したくなっても書き換えずに済みます。簡単形は列名を 1 回しか書かなくてよいぶん、値の対応表を書くときに読みやすくなります。

1 行ずつ、何をしているか
  1. SELECT name,元の列
  2. CASE WHEN salary >= 700000 THEN 'A'上から順に判定する
  3. WHEN salary >= 500000 THEN 'B'上が偽なら次を見る
  4. ELSE 'C'どれにも当てはまらないとき
  5. END AS kaikyu作った列に名前を付ける
  6. FROM shainどの表を見るか

END で閉じるのを忘れやすい部分です。AS で名前を付けないと、製品によっては列名が case のような機械的な名前になります。

検索形(範囲で分ける)
SELECT name, salary,
       CASE WHEN salary >= 700000 THEN 'A'
            WHEN salary >= 500000 THEN 'B'
            ELSE 'C'
       END AS kaikyu
FROM shain
ORDER BY salary DESC;
namesalarykaikyu
鈴木980000A
佐藤720000A
山田700000A
清水650000B
加藤610000B
伊藤530000B
中村530000B
田中520000B

18 … うち先頭 8 行を表示

18 行そのままで、右端に列が 1 つ増えています。A が 3 人・B が 7 人・C が 8 人です。

簡単形(値の対応表)
SELECT name, busho_id,
       CASE busho_id WHEN 1 THEN '経営'
                     WHEN 2 THEN '開発'
                     ELSE 'その他'
       END AS busho
FROM shain
ORDER BY shain_id;
namebusho_idbusho
鈴木1経営
佐藤2開発
田中2開発
中村2開発
小林2開発
加藤5その他

18 … うち先頭 6 行を表示

列名 busho_id を 1 回しか書いていません。等値比較だけなら、こちらのほうが対応関係が読み取りやすくなります。

02⚠️ よくある間違い:上から順に評価される

結論: 最初に真になった枝で確定し、それ以降は見ません。 そのため条件の順序で結果が変わります

範囲で分けるときは、狭いほう(厳しいほう)から書くのが鉄則です。広い条件を先に書くと、そのあとの枝には永久に届きません

次の例は、さきほどの CASE の 1 つ目と 2 つ目を入れ替えただけです。salary >= 500000 が先に来るため、給与が 98 万の人も 72 万の人もすべて B になり、A が 1 人も出ません

エラーは出ません。 分類の結果を数えて初めて気づくたぐいの間違いです。書いたら必ず、各区分の件数を数えて確かめてください。

順序を入れ替えると A が消える
SELECT name, salary,
       CASE WHEN salary >= 500000 THEN 'B'
            WHEN salary >= 700000 THEN 'A'
            ELSE 'C'
       END AS kaikyu
FROM shain
ORDER BY salary DESC;
namesalarykaikyu
鈴木980000B
佐藤720000B
山田700000B
清水650000B
加藤610000B
伊藤530000B
中村530000B
田中520000B

18 … うち先頭 8 行を表示

給与 980000 の鈴木まで B になっています。2 つ目の枝(A)には永久に届きません。18 行のうち A は 0 人です。

03ELSE を省略すると NULL になる

結論: どの枝にも当てはまらず ELSE も無いとき、CASE は NULL を返します。

エラーにはなりません。分類したつもりの列に NULL が並ぶので、そのあとの集計や結合で静かに行が落ちる原因になります。

ELSE は基本的に必ず書いてください。 「その他」を明示しておけば、想定外の値が入ってきたときも NULL ではなくラベルとして現れます。

逆に「当てはまらないものは NULL でよい」と分かっていて省略するのは正しい使い方です。集計と組み合わせる書き方(後述)はこれを利用しています。

ELSE が無いと残りは NULL
SELECT name, salary,
       CASE WHEN salary >= 700000 THEN '高' END AS kekka
FROM shain
ORDER BY salary DESC;
namesalarykekka
鈴木980000
佐藤720000
山田700000
清水650000NULL
加藤610000NULL
伊藤530000NULL

18 … うち先頭 6 行を表示

70 万未満の 15 人は NULL になっています。「分類できなかった」ことがエラーではなく NULL で表れます。

04⚠️ よくある間違い:簡単形は NULL に一致しない

結論: CASE 列 WHEN NULL THEN … は絶対に一致しません。

簡単形の WHEN は内部で 列 = 値 の比較をしています。列 = NULL は真にならず unknown になるので、その枝は選ばれません。

「未設定なら『未設定』と出したい」と思って WHEN NULL と書いても、その行は ELSE に落ちます。エラーが出ないので、ラベルが違うことに気づきにくい部分です。

NULL を判定したいときは検索形で WHEN 列 IS NULL THEN … と書きます。 これは IS NULL を使うので正しく一致します。

所属が未設定なのに「あり」と出る
SELECT name, busho_id,
       CASE busho_id WHEN NULL THEN '未設定'
                     ELSE 'あり'
       END AS hantei
FROM shain
WHERE busho_id IS NULL
ORDER BY shain_id;
namebusho_idhantei
渡辺NULLあり
大野NULLあり

2

取り出しているのは所属が未設定の 2 人だけなのに、判定は「あり」になっています。WHEN NULL が一致していません。

検索形 + IS NULL なら正しく出る
SELECT name, busho_id,
       CASE WHEN busho_id IS NULL THEN '未設定'
            ELSE 'あり'
       END AS hantei
FROM shain
WHERE busho_id IS NULL
ORDER BY shain_id;
namebusho_idhantei
渡辺NULL未設定
大野NULL未設定

2

同じ 2 人が「未設定」になりました。NULL を扱うときは検索形と IS NULL の組み合わせを使ってください。

05式が書ける場所ならどこでも使える

結論: CASE は「値を返す式」なので、SELECT だけでなく ORDER BY や集約関数の中にも書けます。

使い道として特に多いのが次の 2 つです。

  • 業務上の並び順を作る … 文字コード順でも五十音順でもない「決められた順」に並べたいとき、ORDER BY CASE … で順位を作ります
  • 条件つきで数えるsum(CASE WHEN 条件 THEN 1 ELSE 0 END) で「条件に当てはまる行の数」を、まとまりごとに数えられます。WHERE で絞ると他の行まで消えてしまう場面で使います

2 つ目は、全体の件数と条件つきの件数を同じ 1 回の問い合わせで並べて出せるのが利点です。

業務上の並び順にする
SELECT busho_name
FROM busho
ORDER BY CASE busho_name WHEN '経営' THEN 1
                         WHEN '開発' THEN 2
                         WHEN '営業' THEN 3
                         ELSE 9
         END,
         busho_id;
busho_name
経営
開発
営業
管理
基盤
法務
マーケ

7

経営 → 開発 → 営業 の順に並び、残りは busho_id 順です。文字コード順とは無関係な、決められた順序を作れます。

全体と条件つきを並べて数える
SELECT busho_id,
       count(*) AS zenin,
       sum(CASE WHEN salary >= 500000 THEN 1 ELSE 0 END) AS koukyuu
FROM shain
WHERE busho_id IS NOT NULL
GROUP BY busho_id
ORDER BY busho_id;
busho_idzeninkoukyuu
111
243
342
432
521
721

6

部署ごとに「全員の数」と「50 万以上の人数」が並びます。WHERE で 50 万以上に絞ってしまうと全員の数が出せません。

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

ここで出てくる関数を引く

関連するトピック

  • 比較演算子CASE の条件に書く比較そのものの話です。
  • IS NULL簡単形が NULL に一致しない理由と、正しい書き方です。
  • GROUP BY 句条件つきで数えるときに組み合わせます。
  • ORDER BY 句CASE で業務上の並び順を作れます。

根拠(一次情報)