[MySQL] float型の使い方には要注意?!

※当サイトでは広告を掲載しています

MySQLだけに限った話ではないですが、float型というのは、使い方によってはとても危険な型なんだそうです。

簡単に言えば、「float型の1.1は、正確には1.1ではない」というように。

mysql> show columns from test;
+-------+--------+------+-----+---------+-------+
| Field | Type   | Null | Key | Default | Extra |
+-------+--------+------+-----+---------+-------+
| num1  | float  | NO   |     | NULL    |       |
| num2  | double | NO   |     | NULL    |       |
+-------+--------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> insert into `test` ( num1, num2 ) values ( 1.1, 1.1 );
Query OK, 1 row affected (0.04 sec)

mysql> select * from test where num1 = 1.1;
Empty set (0.00 sec)

mysql> select * from test where num2 = 1.1;
+------+------+
| num1 | num2 |
+------+------+
|  1.1 |  1.1 |
+------+------+
1 row in set (0.00 sec)

mysql> select num1 + 0, num2 + 0 from test;
+-------------------+----------+
| num1 + 0          | num2 + 0 |
+-------------------+----------+
| 1.100000023841858 |      1.1 |
+-------------------+----------+
1 row in set (0.00 sec)

こんなfloat型ですが、自分が受け継いだシステムで使われていたので、今回ちょっと不思議な出来事に遭遇しました。

もしかしたら当たり前のことかもしれませんが、自分は初めて遭遇した事態だったので、今後のためにもメモメモ。

サーバ環境を新しくしたらエラーが出た

早い話が、float型のテーブルカラムに空データを入れようとしたら、CentOS6環境では問題なかったのに、CentOS7環境ではエラーになってしまった、という状態。

CentOS6の環境

# httpd -v
Server version: Apache/2.2.15 (Unix)
Server built: Jun 19 2018 15:47:03
# php -v
PHP 5.6.40 (cli) (built: Jan 9 2019 12:04:18)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
# mysql -V
mysql Ver 14.14 Distrib 5.6.43, for Linux (i686) using EditLine wrapper

CentOS7の環境

# httpd -v
Server version: Apache/2.4.6 (CentOS)
Server built: Nov 5 2018 01:47:09
# php -v
PHP 7.3.2 (cli) (built: Feb 5 2019 13:10:03) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.2, Copyright (c) 1998-2018 Zend Technologies
# mysql -V
mysql Ver 14.14 Distrib 5.6.43, for Linux (x86_64) using EditLine wrapper

MySQLに用意したテーブル

状況を再現するために用意したテーブル。

CREATE TABLE `test_info` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `name` varchar(128) NOT NULL COMMENT '名前',
  `size` float NOT NULL DEFAULT '0' COMMENT 'サイズ',
  `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登録日',
  `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='テスト';

CentOS6での動き

CentOS6の環境で、このテーブルに対して以下の処理をPHPから実行。

$db = new PDO( 'mysql:host='ホスト名';dbname='DB名';port=3306;', 'DBユーザー名', 'DBユーザーパスワード' );
$stmt = $db->prepare( "INSERT INTO test_info (name,size) VALUES (:name,:size)" );
$stmt->bindValue( ":name", 'なまえ' );
$stmt->bindValue( ":size", '' );
$stmt->execute();

細かい処理は端折っていますが、こんな感じの処理で、特にワーニングも出ずに挿入や更新ができていました。

CentOS7での動き

しかし、CentOS7の環境で、同様の処理を実行すると、

Warning: 1265 Data truncated for column 'size' at row 1

というワーニングが発生して、結果レコードの挿入や更新ができていませんでした。

本来このワーニングは、長すぎる値を切り詰めるものだと思っていましたが、空のデータを渡したときにも出るんですね。

解決法

結論から言うと、以下の部分を書き換えることで解決しました。

$stmt->bindValue( ":size", '' );
        ↓
if( empty( $size ) || intval( $size ) === 0 ) $size = (float)0;
$stmt->bindValue( ":size", $size );

テーブルの構成で「float DEFAULT '0'」を指定しているのに、空文字を渡しちゃうと「0」がセットされないのは、なんでだろう~。

調べている時間がないので、結果だけメモメモ。

初稿:2019年2月20日

コメント

タイトルとURLをコピーしました