SQLモードとはMySQLの動作を設定する機能で、
MySQL特有の挿入時の値変換をとめたり、「||」をパイプとして使えたり、
できて、他のRDBMSに動作を似せたり、MySQLの旧バージョンに動作を似せたりできる。
値変換の件は前回紹介したsakaik氏のブログに詳しいので詳しく書く必要はないと思う。
今回は日付型に関するSQLモードについて検証してみた結果を吐き出そうと思う。
「んなもんマニュアル読めばわかるっつーの」っていう方はどうぞお引取りください。
役立つ情報はほとんどないと言い切れます。
SQLモードの設定方法
1.
my.conf(my.ini)に以下のように記述した状態でMySQLを起動する。
sql-mode="設定したいSQLモードを「,」区切りで列挙"
2.
MySQL接続したときに、以下のSQLを先に実行する。
SET sql_mode = "設定したいSQLモードを「,」区切りで列挙";
ちなみにWindows msi版はデフォルトで「STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION」が設定されている。
インストールのときに設定するか聞かれるUIがあり、デフォルトではチェックが入っているが見逃しやすいかも。
日付に関係するSQLモード
「ALLOW_INVALID_DATES」
日付の完全チェックを行わずに、月は1から12まで、日は1から31までであることだけをチェックする。
つまり「2010-02-31」のような日付も許可する。
「NO_ZERO_DATE」
「0000-00-00」を許可しない。
「NO_ZERO_IN_DATE」
0月および0日を許可しない。
そして直接日付型とは関係ないが、重要なのが
「STRICT_TRANS_TABLES」および「STRICT_ALL_TABLES」。
指定された値をテーブルに挿入できないときにクエリの実行を中断する。
前者はトランザクションのストレージエンジンに対して、
後者はすべてのストレージエンジンに対して有効。
これらのいずれかが指定されている状態を「STRICTモード」と呼ぶ。
おさらいすると
通常、不正な日付を日付型に挿入しようとすると
MySQLが勝手に「0000-00-00」に変換して挿入してしまう。
実はここで警告を吐いているが、クエリ自体は成功しているので見逃しやすい。
これをSTRICTモードにすると、クエリの実行を中断してエラーを発生させる。
ということ。
ここからが本題
ではSTRICTモードと各日付型に関するSQLモードを組み合わせるとどうなるのか。
また、組み合わせないとどうなるのか。
いくつかのパターンで検証してみた。特に、実在しない日付周りを中心に。
今回の検証ではトランザクションを組む必要もないと思うので、
STRICTモードはすべて「STRICT_ALL_TABLES」で設定する。
テーブル定義
CREATE TABLE `db1`.`tb1` ( `date` DATE NULL ) ENGINE = INNODB;なお発行前に毎回テーブルを空にする。「DELETE FROM `db1`.`tb1`;」
また、ワーニングやエラーが発生した場合は「SHOW WARNINGS;」を発行する。
2010-02-31という実在しない日付の挿入を検証
SQLモードなし
mysql> SET sql_mode = ""; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `db1`.`tb1` ( `date` ) VALUES ( "2010-02-31" ); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> SHOW WARNINGS; +---------+------+-------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------+ | Warning | 1265 | Data truncated for column 'date' at row 1 | +---------+------+-------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT * FROM `db1`.`tb1`; +------------+ | date | +------------+ | 0000-00-00 | +------------+ 1 row in set (0.00 sec)警告あり、値は0000-00-00に変換
STRICTモードのみ
mysql> SET sql_mode = "STRICT_ALL_TABLES"; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `db1`.`tb1` ( `date` ) VALUES ( "2010-02-31" ); ERROR 1292 (22007): Incorrect date value: '2010-02-31' for column 'date' at row 1 mysql> SHOW WARNINGS; +-------+------+---------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------+ | Error | 1292 | Incorrect date value: '2010-02-31' for column 'date' at row 1 | +-------+------+---------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT * FROM `db1`.`tb1`; Empty set (0.00 sec)エラーで挿入できず
ALLOW_INVALID_DATESのみ
mysql> SET sql_mode = "ALLOW_INVALID_DATES"; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `db1`.`tb1` ( `date` ) VALUES ( "2010-02-31" ); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM `db1`.`tb1`; +------------+ | date | +------------+ | 2010-02-31 | +------------+ 1 row in set (0.00 sec)値変換なし、警告なし、エラーなし
STRICTモード + ALLOW_INVALID_DATES
mysql> SET sql_mode = "STRICT_ALL_TABLES,ALLOW_INVALID_DATES"; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `db1`.`tb1` ( `date` ) VALUES ( "2010-02-31" ); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM `db1`.`tb1`; +------------+ | date | +------------+ | 2010-02-31 | +------------+ 1 row in set (0.00 sec)値変換なし、警告なし、エラーなし
NO_ZERO_DATEのみ
mysql> SET sql_mode = "NO_ZERO_DATE"; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `db1`.`tb1` ( `date` ) VALUES ( "2010-02-31" ); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> SHOW WARNINGS; +---------+------+-------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------+ | Warning | 1265 | Data truncated for column 'date' at row 1 | +---------+------+-------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT * FROM `db1`.`tb1`; +------------+ | date | +------------+ | 0000-00-00 | +------------+ 1 row in set (0.00 sec)値変換あり、警告あり、エラーなし
これだけ見ると、「NO_ZERO_DATE」意味ないじゃんってことになりかねない。
なぜなら「0000-00-00」を許可してしまっているから。
が、「NO_ZERO_DATE」の本来の使い道はこうではない。
長くなってしまったので、次回に続けよう。
次回の宿題
・「NO_ZERO_DATE」の本来の使い道・そもそも「0000-00-00」ってなんなんだぜ
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。