日付が直近のレコードと比較してレーティングを求める
ミックさんの本を読み進めました。
達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)
- 作者: ミック
- 出版社/メーカー: 翔泳社
- 発売日: 2008/02/07
- メディア: 単行本(ソフトカバー)
- 購入: 54人 クリック: 1,004回
- この商品を含むブログ (78件) を見る
例えば、ブログの更新日とふぁぼられた数をテーブルに保存しておいて、
直近の日付のレコードと比べて増えた・減った・変わらずというレーティングを算出するときどういうSQLを書けばいいでしょうか?
データとしてはこんなデータで、
mysql> select * from blog_entry; +------------+------------+------+ | written_by | entry_date | favs | +------------+------------+------+ | ギイ | 2011-11-01 | 10 | | ギイ | 2011-11-02 | 10 | | ギイ | 2011-11-03 | 10 | | 託生 | 2011-11-10 | 50 | | ギイ | 2011-11-20 | 5 | | 託生 | 2011-11-22 | 70 | | ギイ | 2011-12-10 | 6 | | 託生 | 2011-12-12 | 20 | | ギイ | 2011-12-30 | 6 | | 託生 | 2011-12-30 | 30 | +------------+------------+------+ 10 rows in set (0.01 sec)
結果としてはこんな感じ。
+------------+------------+------+--------+ | written_by | entry_date | favs | rating | +------------+------------+------+--------+ | ギイ | 2011-11-01 | 10 | - | | ギイ | 2011-11-02 | 10 | → | | ギイ | 2011-11-03 | 10 | → | | ギイ | 2011-11-20 | 5 | ↓ | | ギイ | 2011-12-10 | 6 | ↑ | | ギイ | 2011-12-30 | 6 | → | | 託生 | 2011-11-10 | 50 | - | | 託生 | 2011-11-22 | 70 | ↑ | | 託生 | 2011-12-12 | 20 | ↓ | | 託生 | 2011-12-30 | 30 | ↑ | +------------+------------+------+--------+ 10 rows in set (0.00 sec)
このデータの嫌なところは、
必ず前日のレコードがあるとは限らず、日付が飛んでしまう場合があるってことです。
こういうときは
- 自己相関サブクエリ
- LEFT OUTER JOIN
この二つを使うとうまくいきます☆
僕が書いたSELECT文はこんなやつです。
SELECT B1.written_by, B1.entry_date, B1.favs, CASE WHEN B1.favs = B2.favs THEN '→' WHEN B1.favs > B2.favs THEN '↑' WHEN B1.favs < B2.favs THEN '↓' ELSE '-' END rating FROM blog_entry B1 LEFT OUTER JOIN blog_entry B2 ON B1.written_by = B2.written_by AND B2.entry_date = ( SELECT MAX(entry_date) as entry_date FROM blog_entry B3 WHERE B1.written_by = B3.written_by and B1.entry_date > B3.entry_date GROUP BY B3.written_by ) ORDER BY B1.written_by, B1.entry_date ;
わけがわからないので図解します ToT
答えだけいきなり見せられても意味が解りませんよね;;
この結果を取得するためには3つのステップを踏みます。
- 相関サブクエリを使って「直近の日付」を求める。
- 「直近の日付」を保持する集合を作る。
- その集合ともとの集合を比較する。
ちょっと図を書いてみました。これでわかってもらえるかな??
ところが、これだと直近の日付を保持しておけないので、別の集合を用意します。
ちなみに、本で紹介されているのはちょっとだけ違う例です。
(「ギイ」とかミックさんの本にはもちろん出てきませんw)
「直近の日付」を求めるためにB3を使って、更にB2を比較の為に使うっていう発想が僕にはなかったので勉強になりました。
最初、B1とB2だけで求めようと思ってJOINと相関サブクエリをいじっていたんですけど、うまくいきませんでした。
もうひとつ、B3を使って、B1とB3、B1とB2を結びつけるんだってわかったときは「へぇ〜」って思いました。
初回登場データを漏らさない為にLEFT OUTER JOINを使う
LEFT OUTER JOINをINNER JOINに変えると
+------------+------------+------+--------+ | written_by | entry_date | favs | rating | +------------+------------+------+--------+ | ギイ | 2011-11-02 | 10 | → | | ギイ | 2011-11-03 | 10 | → | | ギイ | 2011-11-20 | 5 | ↓ | | ギイ | 2011-12-10 | 6 | ↑ | | ギイ | 2011-12-30 | 6 | → | | 託生 | 2011-11-22 | 70 | ↑ | | 託生 | 2011-12-12 | 20 | ↓ | | 託生 | 2011-12-30 | 30 | ↑ | +------------+------------+------+--------+ 8 rows in set (0.00 sec)
この通り、初回登場のレコードが出なくなってしまいます。
言い換えれば、LEFT OUTER JOINにするとこの問題を解決できるので覚えておくといいと思います☆