ぱろっと・すたじお

技術メモなどをまったりと / my site : http://parrot-studio.com/

MySQLの困った仕様

まずはこれをご覧ください

require 'date'
Date.parse('0000-00-00 00:00:00') rescue p $!
#=> #<ArgumentError: invalid date>

これが意味するのは、「'0000-00-00 00:00:00'なんて日付は知らねー」ということです
普通はこんな日付を指定する方に問題があるので、
気にすることはないのですが、MySQLが絡むと話は変わってきます


MySQLのDATETIME型において、「未定義」あるいは「解釈不能」の意味で、
この値が格納されることがあります
これを日付型オブジェクトに変換しようとすれば、当然落ちます


MySQL/Rubyの作者もこの問題は認識しているようで、
そのためにMysql::Timeというクラスを用意しているそうです


tmtm日記(2008-11-16)
MySQL/Ruby


ただ、DBIを経由する場合、Mysql::Timeを無視して、
Date.parseを走らせてしまうようなのですΣ(・ω・ノ)ノ


大雑把にしか読んでませんが、この辺が関係ありますかね

Re: Finding out what row DBI chokes on?


解決策としては・・・


 1. '0000-00-00 00:00:00'という値を使わない


そもそも、この値はSQLの仕様外であり、MySQL独自の仕様です
であれば、(DBIを使うくらいだし)他DBとの互換性を考えて、
そもそもNULLを格納するようにするのが手かと


連携元側では意味のある値だとしても、こちら側ではNULLと同じ意味なので、
メインシステムを通す前に、強制的にUPDATEをかけて、とりあえずの対応としました


 2. DBIを使わない(MySQL/Rubyを生で使う)


いくらMySQL独自の仕様とはいえ、「NULL」と「未定義」は違うのだから、
きちっと区別できるMysql::Timeを使う方法です
他のDBのことを考えなければこれも手です


私が開発しているシステムでは、DBのコネクションは抽象化されてるので、
内部実装だけDBIからMySQL/Rubyに書き換えれば、
メインロジックの修正が不要で、根本対応としてはこの方針で考えてます



最後に気になるのがRailsのことですが、
DBIMySQL/Rubyが両方あったときに、
Railsがどっちを使っているかの問題かと


Rails単体で完結する場合、DateTimeで扱えない値は使えないはずなので、
'0000-00-00 00:00:00'が出てくることはないはずですが、
今回のように外部システムと連動する場合が厄介ですね


Rubyist Magazine - RubyOnRails を使ってみる 【第 1 回】


るびま」で見る限り、DBIは不要というか、使ってないように見えますが・・・
機会があれば調べてみます




参考までに、同じような問題はJava(のMySQLドライバ)でも発生するらしく、
ドライバにパラメータを渡すことでNULLか'0001-01-01'にできるそうです
(デフォルトは例外をthrow)


なぜデフォルトで例外を投げるのかというと、
「0001-01-01としても、NULLとしても、元の意味と変わってしまうから」だそうです