ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

MySQLにおけるHAVINGの動作について (SQL Hacks - Hack #3 条件付きINSERTコマンドを実行する)

課題図書

SQL Hacks ―データベースを自由自在に操るテクニック

SQL Hacks ―データベースを自由自在に操るテクニック

お題

貸し出しデータを格納するテーブル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句の中を真偽値で判定は正しそうな予感。

ちなみにOracle

ORA-00920: 関係演算子が無効です。

というエラーが出て実行できません。(安心)

というわけで

これ誤植じゃないの?(もしくは古いバージョンでは動いてた??)
正誤表探しても見つからないのよね。。

正誤表(本家)