MySQLにおけるHAVINGの動作について (SQL Hacks - Hack #3 条件付きINSERTコマンドを実行する)
課題図書
SQL Hacks ―データベースを自由自在に操るテクニック
- 作者: Andrew Cumming,Gordon Russell,西沢直木
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/07/28
- メディア: 単行本(ソフトカバー)
- 購入: 3人 クリック: 90回
- この商品を含むブログ (34件) を見る
お題
貸し出しデータを格納するテーブルlibraryLoanに対して以下のSQLを実行すると、
返却期限が過ぎている場合に、罰金額が表示されるというのだが、
mysql> SELECT 0.20 fine -> FROM libraryLoan -> WHERE member='jim01' AND book='bk002' -> GROUP BY member, book -> HAVING MAX(dueDate) Empty set (0.00 sec) 返却期限が今日だったか、先の日付だったので、jim01は罰金を逃れる。
HAVING MAX(dueDate)
っていう記述で、比較演算子を省略しちゃってるのが不思議。
書くならこうじゃないの?
SELECT 0.20 fine FROM libraryLoan WHERE member='jim01' AND book='bk002' GROUP BY member, book HAVING MAX(dueDate) > CURRENT_DATE
なんでこのSQLで、その要件が満たされるのか、さっぱりわからなかったので検証してみた。
使ったデータベースは、
Server version: 5.1.50-community MySQL Community Server (GPL)
on Windows XPです。
ちなみに本書で使っているMySQLのバージョンは、
MySQLの例は、バージョン5.0前後を対象にしている。
とのことです。
準備
まずはテーブルを作って、データを入れる。
mysql> create table libraryLoan -> ( member char(10) -> , book char(10) -> , dueDate date); Query OK, 0 rows affected (0.05 sec) mysql> insert into libraryLoan values('jim01', 'bk002', '2005-03-22'); Query OK, 1 row affected (0.03 sec) mysql> insert into libraryLoan values('jim01', 'bk002', '2005-09-21'); Query OK, 1 row affected (0.00 sec) mysql> insert into libraryLoan values('jim01', 'bk002', '2006-07-28'); Query OK, 1 row affected (0.02 sec)
早速問題のSQLを実行
mysql> select 0.20 fine -> from libraryLoan -> where member='jim01' and book = 'bk002' -> GROUP BY member, book -> having max(dueDate); +------+ | fine | +------+ | 0.20 | +------+ 1 row in set (0.00 sec)
あれ?
書籍内の結果とは異なり、罰金が表示されてしまいました。。
じゃあ、CURRENT_DATEの比較を入れたらどうか
mysql> select 0.20 fine -> from libraryLoan -> where member='jim01' and book = 'bk002' -> GROUP BY member, book -> having max(dueDate) > current_date; Empty set (0.03 sec)
これは期待通りに動作しました。
(注:current_date = '2011/03/30'です。)
つまり、こういうこと?
要は、HAVING句の中を真偽値で判定してるってことなんじゃないかと予想。
だから、max(dueDate)は、'2006-07-28'なんですが、
真偽値でいうと、(暗黙に変換されて)真と判定され、動いたのではないかと。
で、こんなSQLを実行。
mysql> select 0.20 fine -> from libraryLoan -> where member='jim01' and book = 'bk002' -> GROUP BY member, book -> having true; +------+ | fine | +------+ | 0.20 | +------+ 1 row in set (0.00 sec)
こっちのSQLも実行。
mysql> select 0.20 fine -> from libraryLoan -> where member='jim01' and book = 'bk002' -> GROUP BY member, book -> having false -> ; Empty set (0.00 sec)
というわけで、HAVING句の中を真偽値で判定は正しそうな予感。