【MySQL】【Laravel】「0000-00-00 00:00:00」の日付型データについて
こんにちは、スズキです。
今回は「0000-00-00 00:00:00」という日付データについてです。
また、以前書いた「【Laravel】DBから取得した日付型データを文字列に変換する」という記事について、間違った内容をお伝えしていたため、以前書いた記事に対し、一部訂正を記載しています。
先日開発をしていて躓いた話です。
私が利用している開発環境がPHPStormなのですが、PHPStorm上でDBデータを見ると「NULL」になっている日付型データに対し、「<<対象日付フィールド>> IS NOT NULL」の条件を指定しているのに取得対象から「NULL」のデータが外せないという現象に遭遇しました。
当然「何故?」となったわけで、色々と調べてみたのですが、
結論からいうと、PHPStormが「0000-00-00 00:00:00」というデータを「NULL」と表示する仕様になっていたのが原因でした。
で、この「0000-00-00 00:00:00」というデータ、ネットで調べて見るとテーブルの作り方によって「IS NULL」「IS NOT NULL」のどちらにも引っかかる可能性があるみたいなんですよね。
(だから、PHPStormでは「0000-00-00 00:00:00」を「NULL」と表示しているのだと思います)
「0000-00-00 00:00:00」の挙動についてはこちらの記事をメインで参考にさせていただいたのですが、
リンク先の記事を読む感じだと、以下のようにIS NULL/IS NOT NULLの挙動が変わるようです。
・日付型フィールドがNULLを許容している場合
・IS NULLの対象
・NULL データ
・IS NOT NULLの対象
・「0000-00-00 00:00:00」
・通常の日付データ(「2000-01-01」のようなデータ)
・日付型フィールドがNOT NULL制約付きの場合
・IS NULLの対象
・「0000-00-00 00:00:00」
・IS NOT NULLの対象
・「0000-00-00 00:00:00」が引っかかったり、引っかからなかったり
・通常の日付データ(「2000-01-01」のようなデータ)
MySQLさん何考えてるんでしょうね・・・
まあ、それで、「0000-00-00 00:00:00」というデータですが、数値的には「0」という値のようです。
そのため、テーブル定義にかからわず「NULL」と「0000-00-00 00:00:00」をどちらもNULL値と考えてWhere条件を記載したい場合は、ひとまず以下のようなWhere条件を記載しておけば良いみたいです。
■ NULLを非対象
さて、上記に書いた「0000-00-00 00:00:00」データの存在ですが、この話を知ったとき、「ひょっとして以前書いた記事(【Laravel】DBから取得した日付型データを文字列に変換する)の内容って間違ってたかな?」と不安になり、再度調べ直しました。
結果、間違ってました。
うわぁぁ、恥ずかしい・・・・
前回記事で書いてた、「Illuminate\Database\Eloquent\Model」を継承したモデルクラスで$datesを設定した日付型フィールドのSelect時の取得値ですが、動作確認してみると以下のようなパターンになっていました。
・元データがNULL
→「NULL」を取得
・元データが「0000-00-00 00:00:00」
→「-0001-11-30」のようなCarbon値(year値が-1の値のデータ)
・通常の日付データ(「2000-01-01」のようなデータ)
→通常のCarbon値(「2000-01-01」のようなデータ)
どうして「0000-00-00 00:00:00」が「year値=-1」のデータとして取得されるかは謎です。
(私が動作確認した際にパターン漏れしてるだけで、ひょっとすると「year値=0」になるパターンもあったりするのかもしれません。)
前回書いてた日付文字列取得用のメソッドも取得値がNULLのパターンも考慮する必要があるので、最終的には以下のような感じに修正する必要があります。
嘘記事書いてすみませんでした・・・
ちなみに、どうして「0000-00-00 00:00:00」なんてデータが開発環境のテストデータに存在していたかですが、
どうも動作確認して調べた感じだと、LaravelのDB登録処理を利用している場合、空文字(未入力値)が0として登録されるからみたいです。
「登録時にエラーが出てないし、PHPStorm上でNULLって表示されているからNULLが登録されてるんだろうな」と思ってたデータの中身が「0000-00-00 00:00:00」だったという話でした。
対策しました
今回は「0000-00-00 00:00:00」という日付データについてです。
また、以前書いた「【Laravel】DBから取得した日付型データを文字列に変換する」という記事について、間違った内容をお伝えしていたため、以前書いた記事に対し、一部訂正を記載しています。
「0000-00-00 00:00:00」の日付型データ
先日開発をしていて躓いた話です。
私が利用している開発環境がPHPStormなのですが、PHPStorm上でDBデータを見ると「NULL」になっている日付型データに対し、「<<対象日付フィールド>> IS NOT NULL」の条件を指定しているのに取得対象から「NULL」のデータが外せないという現象に遭遇しました。
当然「何故?」となったわけで、色々と調べてみたのですが、
結論からいうと、PHPStormが「0000-00-00 00:00:00」というデータを「NULL」と表示する仕様になっていたのが原因でした。
で、この「0000-00-00 00:00:00」というデータ、ネットで調べて見るとテーブルの作り方によって「IS NULL」「IS NOT NULL」のどちらにも引っかかる可能性があるみたいなんですよね。
(だから、PHPStormでは「0000-00-00 00:00:00」を「NULL」と表示しているのだと思います)
「0000-00-00 00:00:00」の挙動についてはこちらの記事をメインで参考にさせていただいたのですが、
リンク先の記事を読む感じだと、以下のようにIS NULL/IS NOT NULLの挙動が変わるようです。
・日付型フィールドがNULLを許容している場合
・IS NULLの対象
・NULL データ
・IS NOT NULLの対象
・「0000-00-00 00:00:00」
・通常の日付データ(「2000-01-01」のようなデータ)
・日付型フィールドがNOT NULL制約付きの場合
・IS NULLの対象
・「0000-00-00 00:00:00」
・IS NOT NULLの対象
・「0000-00-00 00:00:00」が引っかかったり、引っかからなかったり
・通常の日付データ(「2000-01-01」のようなデータ)
MySQLさん何考えてるんでしょうね・・・
まあ、それで、「0000-00-00 00:00:00」というデータですが、数値的には「0」という値のようです。
そのため、テーブル定義にかからわず「NULL」と「0000-00-00 00:00:00」をどちらもNULL値と考えてWhere条件を記載したい場合は、ひとまず以下のようなWhere条件を記載しておけば良いみたいです。
■ NULLを非対象
SELECT test_tables.* FROM test_tables WHERE test_tables.test_date > 0 ;
■ NULLのみを対象
SELECT test_tables.* FROM test_tables WHERE (test_tables.test_date = 0 OR test_tables.test_date IS NULL) ;
以前書いた記事内容の訂正について
さて、上記に書いた「0000-00-00 00:00:00」データの存在ですが、この話を知ったとき、「ひょっとして以前書いた記事(【Laravel】DBから取得した日付型データを文字列に変換する)の内容って間違ってたかな?」と不安になり、再度調べ直しました。
結果、間違ってました。
うわぁぁ、恥ずかしい・・・・
前回記事で書いてた、「Illuminate\Database\Eloquent\Model」を継承したモデルクラスで$datesを設定した日付型フィールドのSelect時の取得値ですが、動作確認してみると以下のようなパターンになっていました。
・元データがNULL
→「NULL」を取得
・元データが「0000-00-00 00:00:00」
→「-0001-11-30」のようなCarbon値(year値が-1の値のデータ)
・通常の日付データ(「2000-01-01」のようなデータ)
→通常のCarbon値(「2000-01-01」のようなデータ)
どうして「0000-00-00 00:00:00」が「year値=-1」のデータとして取得されるかは謎です。
(私が動作確認した際にパターン漏れしてるだけで、ひょっとすると「year値=0」になるパターンもあったりするのかもしれません。)
前回書いてた日付文字列取得用のメソッドも取得値がNULLのパターンも考慮する必要があるので、最終的には以下のような感じに修正する必要があります。
/** * 日付文字列を取得します。 */ public function getDateString($dateValue, $stringFormat) { // 元データがNULLの場合 if ($dateValue === null) { return null; } // 元データが「0000-00-00 00:00:00」の場合 // ※補足:Modelクラス側でDateTime型に変換していると(Model->$datesを設定していると)、 // 元データが「0000-00-00 00:00:00」の場合に、 // 本タイミングでは「-0001-11-30 00:00:00.000000」のような値が入っているのでこれを考慮する必要がある $year = $dateValue->year; $minYear = 0; if ($year < $minYear) { return null; } // 文字列変換 $dateString = $dateValue->format($stringFormat); return $dateString; }
嘘記事書いてすみませんでした・・・
余談
ちなみに、どうして「0000-00-00 00:00:00」なんてデータが開発環境のテストデータに存在していたかですが、
どうも動作確認して調べた感じだと、LaravelのDB登録処理を利用している場合、空文字(未入力値)が0として登録されるからみたいです。
「登録時にエラーが出てないし、PHPStorm上でNULLって表示されているからNULLが登録されてるんだろうな」と思ってたデータの中身が「0000-00-00 00:00:00」だったという話でした。
対策しました