【SQL】CASEとWHENによる条件分岐について、さまざまな使い方を紹介
本記事ではCASE式の基本的な使い方や、他の文や句と連携して使用する方法を紹介します。
本記事ではCASE式の基本的な使い方や、他の文や句と連携して使用する方法を紹介します。
スキルアップ
2022/12/26 UP
- プログラミング
- インフラエンジニア
- 開発
SQLで条件分岐を行なうとき、CASE式が活用できます。条件分岐が使いこなせるとSQLで複雑なデータ処理を実現できるようになるため、覚えておくとよいでしょう。本記事ではCASE式の基本的な使い方や、他の文や句と連携して使用する方法を紹介します。
CASEとは
CASEはWHENと合わせて使用し、条件分岐を行なうために用います。他のプログラム言語でいうif, elseと同様の役割があり、データごとに指定した条件に合致するかどうかを判定します。判定結果に応じてデータに個別の処理を加えられるため、活用できる場面は多いでしょう。
同様に、WHERE句を用いた場合も条件を指定してデータの絞り込みができますが、結果に対して何らかの処理を加えることはできません。CASE式は条件に応じて出力結果を変えられる点が異なります。また注意点として、WHENで指定する各条件はわかりやすく、それぞれが排他的であるようにしましょう。SQL文の可読性が良くなり、ミスも減らせます。
本記事では、商品データ(productテーブル)を例に解説します。テーブル内の各データは以下のような内容です。
商品データ(productテーブル)
product_cd (商品コード) |
category_cd (カテゴリコード) |
unit_cost (単価) |
---|---|---|
1001 | 2001 | 300 |
1002 | 2001 | 500 |
1003 | 2002 | 620 |
1004 | 2002 | 450 |
1005 | 2003 | 800 |
1006 | 2003 | 1020 |
CASEの基本形
CASE式は、単純CASE式と検索CASE式の2つに分けられます。それぞれの特徴について解説します。
単純CASE式
CASEの直後に置いた変数または式を、WHENの直後に置いた各条件と比較する記述の仕方が単純CASE式です。各条件との比較結果が"="で一致する場合に、THEN以降の処理を実行します。次に紹介する検索CASE式と比べてコード量が短く、可読性も良いことが利点です。しかし単純CASE式は応用が利きづらく表現の幅が狭くなってしまいます。あとから条件を追加する際に書き直さざるを得ないこともあるでしょう。
次のコードでは、商品データを表すproductテーブルに含まれるcategory_cd(カテゴリコード)に応じたカテゴリ名のカラムを追加しています。CASEの直後にcategory_cdを置き、「WHEN 〇〇 THEN △△」の〇〇にカテゴリコードを、△△に対応するカテゴリ名を設定します。該当するものがない場合は、ELSEの直後に記載したno_categoryを出力します。CASE文の最後にはENDを記載するのを忘れないようにしてください。ENDの直後のASで、CASE文の出力を追加するカラムの名前(category_name)を設定しています。
※サンプルコード
SELECT pr.*, CASE category_cd WHEN 2001 THEN 'meat' WHEN 2002 THEN 'vegetable' WHEN 2003 THEN 'fruit' ELSE 'no_category' END AS category_name FROM product AS pr
行 | product_cd | category_cd | unit_cost | category_name |
---|---|---|---|---|
1 | 1001 | 2001 | 300 | meat |
2 | 1002 | 2001 | 500 | meat |
3 | 1003 | 2002 | 620 | vegetable |
4 | 1004 | 2002 | 450 | vegetable |
5 | 1005 | 2003 | 800 | fruit |
6 | 1006 | 2003 | 1020 | fruit |
出力結果をみると、各カテゴリコードに対応するカテゴリ名がcategory_nameのカラムに追加されました。
検索CASE式
WHENの直後に判定式を置き、式が真である場合にTHEN以降の処理を実行するという記述の仕方が検索CASE式です。単純CASE式のように一致だけでなく、さまざまな条件式で判別できるという利点があります。なお単純CASE式でも同じですが、ELSEの後ろに記載がなければNULLを出力します。
次のコードは、unit_cost(単価)の金額に応じてlow、middle、highという3つのランクを設定しています。WHENの直後に比較演算子を含んだ条件式を置き、500円より小さい、1000円より小さい、その他(1000円以上)という3つに分類しています。CASE文内のWHENは上から順に実行されるため、500未満のデータがmiddleやhighに分類されることはありません。
※サンプルコード
SELECT pr.*, CASE WHEN unit_cost < 500 THEN 'low' WHEN unit_cost < 1000 THEN 'middle' ELSE 'high' END AS lank FROM product AS pr
行 | product_cd | category_cd | unit_cost | lank |
---|---|---|---|---|
1 | 1001 | 2001 | 300 | low |
2 | 1002 | 2001 | 500 | middle |
3 | 1003 | 2002 | 620 | middle |
4 | 1004 | 2002 | 450 | low |
5 | 1005 | 2003 | 800 | middle |
6 | 1006 | 2003 | 1020 | high |
CASEの使い方のヒント
CASEを使う際に役立つ方法や注意点を解説します。データ分析の幅が広がるので、ぜひ参考にしてください。
論理演算子で複数カラムの条件を指定
2つ以上のカラムに関する条件を連結して、1つの条件にすることができます。ANDやORといった論理演算子を用いて「AかつB」「AまたはB」などの形式で条件を指定できます。
商品テーブルのなかから、カテゴリが肉であり、単価が500円より小さいレコードを見つける場合を考えてみましょう。次のコードはcategory_cd(カテゴリコード)が2001、unit_cost(単価)が500より小さいという、2つのカラムに関する条件をANDで連結しています。
※サンプルコード
SELECT pr.*, CASE WHEN category_cd = 2001 AND unit_cost < 500 THEN 1 ELSE 0 END AS meat_under500 FROM product AS pr
行 | product_cd | category_cd | unit_cost | meat_under500 |
---|---|---|---|---|
1 | 1001 | 2001 | 300 | 1 |
2 | 1002 | 2001 | 500 | 0 |
3 | 1003 | 2002 | 620 | 0 |
4 | 1004 | 2002 | 450 | 0 |
5 | 1005 | 2003 | 800 | 0 |
6 | 1006 | 2003 | 1020 | 0 |
CASE式の結果はmeat_under500列に出力しました。条件に当てはまるレコードには1を、当てはまらないものは0を出力します。1行目のレコードのみが条件に当てはまることがわかります。
INで複数条件を指定
特定のカラムについて複数の条件指定を行ないたいとき、IN句を利用してもよいでしょう。論理演算子のORの代わりに利用し、条件文を短くできます。当てはまる条件が複数となる場合はコード量を大幅に減らせるため便利です。該当しない要素を調べたいときはNOT INを用います。
商品テーブル内のproduct_cd(商品コード)について、1001、1003、1005の3つのどれかに該当するレコードを探したい場合を考えます。今回はIN句を用いて3つのproduct_cdを指定し、該当するレコードはproduct_cd_check列に1を、該当しないレコードは0を出力しました。
※サンプルコード
SELECT pr.*, CASE WHEN product_cd IN (1001,1003,1005) THEN 1 ELSE 0 END AS product_cd_check FROM product AS pr
行 | product_cd | category_cd | unit_cost | product_cd_check |
---|---|---|---|---|
1 | 1001 | 2001 | 300 | 1 |
2 | 1002 | 2001 | 500 | 0 |
3 | 1003 | 2002 | 620 | 1 |
4 | 1004 | 2002 | 450 | 0 |
5 | 1005 | 2003 | 800 | 1 |
6 | 1006 | 2003 | 1020 | 0 |
条件に合うデータのみ集計
CASE式で抽出した結果を集計したいとき、集約関数との連携も可能です。例えば、CASE式で条件に一致するとき1、一致しないとき0としてSUM関数に入れると、条件に一致するレコード数を集計できます。ほかにはMAX関数やMIN関数を使ってデータのなかから最大値や最小値を求めることもできます。
商品テーブル内から、unit_cost(単価)が500より大きいレコード数を集計したい場合を考えます。次のコードのように、CASE式を用いてunit_costが500より大きい場合に1を出力させ、CASEからENDまでをSUM関数に入れます。集計した結果にはover500_numというカラム名を付けました。
※サンプルコード
SELECT SUM(CASE WHEN unit_cost > 500 THEN 1 ELSE 0 END) AS over500_num FROM product AS pr
行 | over500_num |
---|---|
1 | 3 |
商品テーブルに含まれるレコードのうち、unit_costが500より大きいのは620、800、1020の3つであり、正確に出力されていることがわかります。
CASEの入れ子
CASE式の条件式に別のCASE式を挿入するという、入れ子構造の使い方ができます。2段階や3段階など複雑な条件分岐の作成が可能です。ただし入れ子にしすぎるとコードの可読性が悪くなるため、他の記述方法で代替できないか先に検討するとよいでしょう。
商品テーブルのunit_cost(単価)の値について、条件に応じて値引きされた値を計算したい場合を考えます。category_cd(カテゴリコード)が2001、2002の商品はunit_costが500より大きければ10%引き。category_cdが2003の商品はunit_costが1000より大きければ2%引きという条件にしました。計算結果はcostカラムに出力するとして、以下のコードで書き表されます。
※サンプルコード
SELECT pr.*, CASE WHEN category_cd IN (2001,2002) THEN CASE WHEN unit_cost > 500 THEN unit_cost*0.9 ELSE unit_cost END WHEN category_cd IN (2003) THEN CASE WHEN unit_cost > 1000 THEN unit_cost*0.8 ELSE unit_cost END END AS cost FROM product AS pr
行 | product_cd | category_cd | unit_cost | cost |
---|---|---|---|---|
1 | 1001 | 2001 | 300 | 300.0 |
2 | 1002 | 2001 | 500 | 500.0 |
3 | 1003 | 2002 | 620 | 558.0 |
4 | 1004 | 2002 | 450 | 450.0 |
5 | 1005 | 2003 | 800 | 800.0 |
6 | 1006 | 2003 | 1020 | 816.0 |
出力結果のcost列を見ると、3行目のレコードは10%引き、6行目のレコードは20%引きとなっており、期待どおりの結果が得られました。入れ子構造のコードを記述する際は、行を字下げするなどして入れ子の塊を区別するとコードが読みやすくなります。
他の文や句との連携
CASE式をWHERE文やUPDATE文、ORDER句などと組み合わせると表現の幅が広がります。連携した場合の使い方についてサンプルコードを用いて解説します。
WHERE文で使用
WHERE文は条件を指定してレコードを絞り込む場合に使用します。WHERE文のなかでCASE式を用いると、データに応じて条件の指定の切り替えが可能です。
以下のコードはunit_cost(単価)が500より小さいとき、category_cdが2001の場合のみ出力します。unit_costが500以上であればELSEの条件となるので、すべて出力されます。
※サンプルコード
SELECT pr.* FROM product AS pr WHERE CASE WHEN unit_cost < 500 THEN category_cd = 2001 ELSE TRUE END
行 | product_cd | category_cd | unit_cost |
---|---|---|---|
1 | 1001 | 2001 | 300 |
2 | 1002 | 2001 | 500 |
3 | 1003 | 2002 | 620 |
4 | 1005 | 2003 | 800 |
5 | 1006 | 2003 | 1020 |
出力結果を見ると、1行目はunit_costが500より小さく、category_cdが2001なので表示されています。しかし元データにあったproduct_cdが1004のレコードは、unit_costが500より小さいですが、category_cdが2002なので結果に出力されませんでした。このようにWHEREのなかでCASE式を用いると複雑な条件分岐を実現できます。
ORDER BY句で使用
ORDER BYを用いるとテーブルのレコードの並び替えができます。ORDER BY内でCASE式を利用し、並び替えの番号を指定すると任意の順序に並び替えられます。
以下のコードは、商品テーブル内のproduct_cd(商品コード)を任意の順番に並び替える方法を示しています。ORDER BY内にCASE式を並べ、各product_cdについて並べる順番を指定しました。
SELECT pr.* FROM product AS pr ORDER BY CASE WHEN product_cd = 1001 THEN 4 WHEN product_cd = 1002 THEN 1 WHEN product_cd = 1003 THEN 5 WHEN product_cd = 1004 THEN 2 WHEN product_cd = 1005 THEN 6 WHEN product_cd = 1006 THEN 3 ELSE 99 END
行 | product_cd | category_cd | unit_cost |
---|---|---|---|
1 | 1002 | 2001 | 500 |
2 | 1004 | 2002 | 450 |
3 | 1006 | 2003 | 1020 |
4 | 1001 | 2001 | 300 |
5 | 1003 | 2002 | 620 |
6 | 1005 | 2003 | 800 |
出力結果を見ると、product_cdが指定した順番に並んでいます。通常はデータが数字であれば番号の昇順または降順で並べることが多いですが、データの中身が文字式の場合など任意の順番に並べたいときは役に立つでしょう。
CASEでさまざまな条件分岐を実現
CASE式を用いると、他言語のif, elseと同様にSQL文のなかで条件分岐を実現できます。指定した条件に応じて処理の内容を変えられるようになるため、データ処理の幅が広がるでしょう。大きく分けて単純CASE式と検索CASE式の2種類がありますが、基本的にはさまざまな条件式を実現できる検索CASE式を活用するとよいでしょう。
WHEREやORDER BYなど他の文や句との連携も可能であるため、さまざまなCASEの使い方を理解しておくと役に立つはずです。ただしCASE文を入れ子にしすぎたり、分岐の条件を複雑にしすぎたりすると可読性が悪くなるので注意してください。