ログイン記事を投稿する会員登録はこちら!ヘルプ

CASE式のススメ(前編)

[初級] 汎用度の高い条件式CASEの代表的な使い方
ミック [著]  | 
評価:3.71 σ = 1.59
公開:06/06/08
Web
DB
EZ新着
SQL-92で標準に取り入れられたCASE式を用いると、今までCASE式の簡略版であるDECODE(Oracle)、IF(MySQL)などの関数で記述していたSQLを、実装非依存のコードに記述し直すことができ、コードの汎用性を高めることができます。今回は、コード体系の変換、集約関数との組み合わせ、CHECK制約との組み合わせの3通りの使い方を学びます。
サンプルファイル 0.7KB (123)
Page 1 / 2 / 3

既存のコード体系を新しい体系に変換して集計

 非定型的な集計を行なう業務では、既存のコード体系を分析用のコード体系に変換して、その新体系の単位で集計したい、という要件が持ち込まれることがあります。

 例えば、県コードは、「1:北海道、2:青森、……47:沖縄」というように振られていますが、これを東北、関東、九州といった地方単位にまとめ、その単位で人口を集計したい場合です。具体的には、次に示す表1の内容を集計し、表2の結果を求めるような場合です。

表1:集計元の表A
県名(pref_name)人口(population)
徳島100
香川200
愛媛150
高知200
福岡300
佐賀100
長崎200
東京400
群馬50
表2:集計結果A
地方名人口
四国650
九州600
その他450

 こんなとき、皆さんならどうしますか? 「地方コード」という列を持つビューを定義する、というのも一つの方法です。しかしそれだと、集計に使いたいコード体系の数だけ列を追加しなければなりませんし、動的な変更も困難です。

 CASE式を使うと次のような一つのSQLで取出しが可能です。ここでは分かりやすくするために、コードの代わりに県名(pref_name)をGROUP BYのキーに使います。

サンプル1
SELECT  SUM(population),
        CASE pref_name
                WHEN '徳島県' THEN '四国'
                WHEN '香川県' THEN '四国'
                WHEN '愛媛県' THEN '四国'
                WHEN '高知県' THEN '四国'
                WHEN '福岡県' THEN '九州'
                WHEN '佐賀県' THEN '九州'
                WHEN '長崎県' THEN '九州'
        ELSE 'その他' END
FROM    Table_A
GROUP BY CASE pref_name
                WHEN '徳島県' THEN '四国'
                WHEN '香川県' THEN '四国'
                WHEN '愛媛県' THEN '四国'
                WHEN '高知県' THEN '四国'
                WHEN '福岡県' THEN '九州'
                WHEN '佐賀県' THEN '九州'
                WHEN '長崎県' THEN '九州'
        ELSE 'その他' END;

 豪快にGROUP BY句にSELECT句のCASE式をコピーしてあげるのがポイントです。単純に「GROUP BY pref_name」と変換前の列を指定すると、正しい結果が得られないので注意してください。

 また、同様の考え方で、数値型の列を適当な階級体系に振り分けて集計することも可能です。例えば、所得階級(salary_class)ごとのレコード数を調べたい場合は、次のようなSQLになります。

サンプル2
SELECT
        CASE WHEN salary <= 500 THEN '1'
             WHEN salary > 500 AND salary <= 600  THEN '2'
             WHEN salary > 600 AND salary <= 800  THEN '3'
             WHEN salary > 800 AND salary <= 1000 THEN '4'
        ELSE NULL END salary_class,
        COUNT(*)
FROM    Table_A
GROUP BY
        CASE WHEN salary <= 500 THEN '1'
             WHEN salary > 500 AND salary <= 600  THEN '2'
             WHEN salary > 600 AND salary <= 800  THEN '3'
             WHEN salary > 800 AND salary <= 1000 THEN '4'
        ELSE NULL END;

異なる条件の集計を1つのSQLで行なう

 異なる条件の集計は、CASE式の使い方として有名なものの一つです。例えば、先の県別人口を保持するテーブルに、性別列を付け加えたテーブルから、男女別・県別の人数の合計を求める、というケースを考えます。具体的には、次に示す表3の内容を集計し、表4の結果を求めるような場合です。

表3:集計元の表B
県名(pref_name)性別(sex)人口(population)
徳島160
徳島240
香川1100
香川2100
愛媛1100
愛媛250
高知1100
高知2100
福岡1100
福岡2200
佐賀120
佐賀280
長崎1125
長崎2125
東京1250
東京2150
表4:集計結果B
県名
徳島6040
香川100100
愛媛10050
高知100100
福岡100200
佐賀2080
長崎125125
東京250150

 普通は次のように、WHERE句で条件分岐させて、2回SQLを発行します。

サンプル3
--男性の人口
SELECT  pref_name,
        COUNT(*)
FROM  Table_A
WHERE sex = '1'
GROUP BY pref_name;

--女性の人口
SELECT  pref_name,
        COUNT(*)
FROM  Table_A
WHERE sex = '2'
GROUP BY pref_name;

 UNIONを使えば1つのSQLにできますが、コストは減りませんし、SQLも無駄に長くなります。一方、CASE式を使えば、次のような1つのSQLで済みます。

サンプル4
SELECT pref_name,
       SUM( CASE WHEN sex = '1' THEN 
                      population ELSE 0 END),  --男性の人口
       SUM( CASE WHEN sex = '2' THEN 
                      population ELSE 0 END)   --女性の人口
FROM  Table_A
GROUP BY pref_name;

 性別が男性'1'のレコードと女性'2'の人口列を、それぞれ合計しているわけです。いわば「行持ち」のデータから「列持ち」に水平展開しているのです。集約関数であれば、SUMに限らずCOUNTでもAVGでも同様に使えます。

 このトリックの重宝するところは、SQLの結果を二次元表の形に整形できることです。単純にGROUP BYで集約しただけだと、その後、ホスト言語やExcelなどのアプリケーション上でクロス表の形に整形しなければなりません。しかし、サンプル4を見ると、表側が県名、表頭が性別という、既に二次元表の形式で結果が出力されることが分かります。これは集計表を作るときに非常に便利な機能です。この技をスローガン的に表現するならば、

WHERE句で条件分岐させるのは素人のやること。プロはSELECT句で分岐させる。

 ということです。使い勝手の良い技なので、多用してください。

DECODE関数の弱点
 DECODE関数がCASE式に比べて劣っていると思う点は、次の4つです。
  • 第1に、Oracleの方言なので互換性がありません。
  • 第2に、分岐の数が127に制限されています(引数の上限数は255ですが、一つの分岐を表現するのに2つの引数を要します)。
  • 第3に、分岐の数が増えるとソースが非常に読みづらくなります。
  • 第4に、記述力が貧弱です。具体的には、引数に述語を使った式を取ることができません。当然、サブクエリを作ることもできません。
←
1 2 3
→
INDEX
CASE式のススメ(前編)
はじめに
必要な環境
対象読者
導入:CASE式とは
Page 2
既存のコード体系を新しい体系に変換して集計
異なる条件の集計を1つのSQLで行なう
CHECK制約で複数の列の条件関係を定義する
まとめ
関連記事
db

変更履歴

2006/06/09 18:43
  • Page1 評価方法の例文のキャプション「WHERE句」を「WHEN句」に修正
評価を送信する


著者紹介
ミック (ミック)
主にOracleを使ったデータウェアハウス業務に従事するDBエンジニア。
HPのコンテンツ『リレーショナル・データベースの世界』。

著書:
達人に学ぶ SQL徹底指南書』(翔泳社、2008)

訳書:
ジョー・セルコ『SQLパズル 第2版』(翔泳社、2007)

講演資料:
「みんなまとめて面倒みよう」(デブサミ2008、2008/02/13)
コメント
(最新日付順:新着コメントRSS配信中!

■自由度の高さが魅力です
コメント評:+0
 
Bad!
 
Good!

そうですね。最初にCASE式を習うときとは、IFやDECODEと同じだと教わるのですが、実はCASE式のがずっと自由度が高くて応用がきくんです。

CASE式を使えるようになると、SQLでできることの幅が広がって、結構楽しくなってきます。まさあきさんも、色々な使い方を試してみてください。
■応用が広がりそうです
コメント評:+0
 
Bad!
 
Good!

すごいです。
いままで、単純にCASEのことをいわゆるIFと、同じだと思って使ってました。

SELECTとGROUPに同時使用することで、こんなにひろがっていくのですね。
・・・感動しました!
名前:*
メールアドレス(名前にリンク):
URL(名前にリンク):
タイトル:
内容(テキストのみ1200文字まで、リンクタグ入力不可):*
アイコン:
なし

利用規約に同意して

トラックバック
この記事のトラックバックURL:
スパム対策で、トラックバックはデフォルトで非公開とし、編集部チェックを通した上で公開させて頂いております。重複した登録などにご注意ください。主な判断基準は、「当該記事へ有効なリンクが張られている」「内容に関連性がある」の2点を満たしていることです。
Trackback (0)
記事は編集作業を経て公開されていますが、あくまで情報提供を第一の目的としたものであり、 内容には、不正確な記述、執筆者の予断や誤解に基づくもの、リンク切れ、環境要件が古いものが含まれていることがあります。 記事(翻訳記事を除く)の訂正に関しては、編集部の判断により随時対応することがありますが、各著作権者および(株)翔泳社はその内容の完全性を一切保障しません。 「投稿」の性質上、各著作権者は読者より訂正の依頼があったとしても対応できないこともあります。 記事内容の運用により派生した損害を含むあらゆる結果について、各著作権者および(株)翔泳社は一切の責任を持ちません。 各著作権者は記事内容に関するあらゆるサポートに付いてもその義務を放棄しています。あくまで記事は投稿され、編集を経て、公開された時点で完結したものであり、公開以降もサポートするかどうかは各者の任意事項となります。あらかじめご了承ください。
最新ニュース ≫一覧
最新記事 ≫一覧
一般投稿
Windows PowerShell 入門(5)−制御構文
Excelマクロによる、seleniumテストケースの自動生成(1)
VB.NET版O/Rマッピングツール「ObjectService」の使い方(バージョン管理)
Windows PowerShell 入門(4)−変数と演算子
JavaScriptとPHPでつくるAjaxインクリメンタル検索
最近のコメント
嘘と誤解を招く表現ばかりであきれます。筆者は自分の世界で扱っていたCSVの仕様を...(anon:05/14)
DocomoはリロードしただけでもIPアドレスが変わることがありますね。 ただ...(F:05/08)
> よくわかるのですが、中継サーバのIPがころころ変更になるキャリアがあって、そ...(佐藤 龍之介:05/07)
編集部ブログ
はじめまして、第2の新人です。()
はじめまして新人です。()
GW期間中のお問い合わせについて()
「Hooray!」…?()
サイトメンテナンスのお知らせ(2008/04/25 18:00〜04/26)()
サイト統計
はてなブックマーク合計数
昨日までの登録メンバー数昨日までの総メンバー数