MySQLには1足せない数値リテラルがある
MySQLの数値リテラルに+1すると怒られたり、オーバーフローしたりすることがあります。下記は僕の手元のMySQL5.5.22での実行結果です。
mysql> select 9223372036854775807+1; ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
もう足せないよ、と怒られてしまいました。しかし、それより1大きい数は+1しても怒られません。
mysql> select 9223372036854775808+1; +-----------------------+ | 9223372036854775808+1 | +-----------------------+ | 9223372036854775809 | +-----------------------+ 1 row in set (0.00 sec)
どうやらMySQLの数値リテラルには型があるようで、演算の途中で値の範囲チェックに引っかかることがあるようです。最初の例の9223372036854775807というのはBIGINTの上限値で、1を足すと怒られてしまうようです。
しかし、もっと大きい数には足し算ができるのに9223372036854775807に足し算ができないのは納得がいきません。この場合、符号変換を2回行うことで足し放題になります。
mysql> select --9223372036854775807+1; +-------------------------+ | --9223372036854775807+1 | +-------------------------+ | 9223372036854775808 | +-------------------------+ 1 row in set (0.00 sec)
さらに、別の場所のMySQL5.1.51で同じ実験をしたところ、下記のようにオーバーフローしてBIGINTの下限値になってしまいました(一見正しい結果に見えますが、なんと負の数になっています)。この場合も1大きい数では問題は起こりません。
mysql> select 9223372036854775807+1; +-----------------------+ | 9223372036854775807+1 | +-----------------------+ | -9223372036854775808 | +-----------------------+ 1 row in set (0.02 sec) mysql> select 9223372036854775808+1; +-----------------------+ | 9223372036854775808+1 | +-----------------------+ | 9223372036854775809 | +-----------------------+ 1 row in set (0.01 sec)
ちなみに、UNSIGNED BIGINTの上限値である18446744073709551615でも同じことが起こります。これらの上限値は900京(900兆の1万倍)や1800京といった超巨大な値ですので、実アプリケーションで問題になることは無いと思いますが、ちょっと気持ち悪い気がしますね。