2017年1月21日土曜日

MySQL では、'a' = 0 が True になる。

オフィス狛 技術部です。

MySQL において、文字列と数値の比較は思わぬ結果になってしまう事があるので注意です。

まず、題名にもある通り、

「SELECT 0 = 'a';」はTrueになります。

・なぜこんな事が起きてしまうのか?

MySQLでは、型が違う値同士を比較すると、暗黙的な変換が発生します。
文字列の「1」は、数値の「1」になります。
しかし、数値に変換出来ないもの、例えば「a」という文字は、
変換出来ないので、「0」になります。
(変換は出来ないけど、型を一緒にする為に、無理やり数値にしてくれる)

変換出来ないなら、諦めて欲しいところなのですが、
頑張り屋さんなんですね、MySQLは。

という訳で、色々試して見た結果が以下の通りです。
SELECT 0 = 0;   -- True になる
SELECT 0 = 1;   -- False になる
SELECT 'a' = 'a'; -- True になる
SELECT 'a' = 'c'; -- False になる
SELECT 0 = '0';  -- True になる
SELECT 0 = 'a';  -- True になる
SELECT 1 = 'a';  -- False になる

やっぱり、「SELECT 0 = 'a'; 」が問題になりそうですね。

・他にもあった、頑張り屋さんならではの弊害

先程、「a」という文字列は数値に出来ませんでした。
では「1a」という文字列ではどうでしょう?
・・・・数値にしてくれるのです。そう、MySQLなら。

この場合、「1a」は「1」に変換されます。

こちらも色々試して見ました。
SELECT 1 = '1'; true
SELECT 1 = '1a'; true
SELECT 1 = '1abcd'; true
SELECT 1 = 'abcd1'; false
どうやら、先頭に数値が来ると、その数値に変換して比較するようです。

・でも、別にMySQLは悪くない

むしろ、頑張り屋さんで、良いと思います。

そもそも、SQLで文字列と数値を比較するような事を発生させないのが筋です。
そして、その制御はSQLに値を設定するプログラム側で行うべきだと思います。
(もっと言うと、そんな事が発生する場合、設計を見直した方が良いのかな、とも思います。)

・その他気になったところ

リファレンスマニュアルには、下記の式についても、 結果が異なると記載されています。
SELECT '18015376320243458' = 18015376320243458; true になる
SELECT '18015376320243459' = 18015376320243459; false になる
これを色々な環境で試してみたのですが、
SELECT '18015376320243458' = 18015376320243458; true
SELECT '18015376320243459' = 18015376320243459; true

間違った判定をされる事はありませんでした。

リファレンスをちゃんと読むと、
さらに、文字列から浮動小数点への変換および整数から浮動小数点への変換は、必ずしも同様に発生するとはかぎりません。整数は、CPU によって浮動小数点に変換される可能性があります。一方、文字列は、浮動小数点の乗算を伴う演算で 1 桁ずつ変換されます。 表示される結果はシステムによって異なり、コンピュータのアーキテクチャーやコンパイラのバージョンなどの要因、または最適化レベルの影響を受ける可能性があります。このような問題を回避する方法の 1 つは、値が暗黙的に浮動小数点値に変換されないように、CAST() を使用することです。
「MySQL 5.6 リファレンスマニュアル」『関数と演算子 / 式評価での型変換 / 12.2 式評価での型変換』より。
2017年1月17日 (火) 08:11 UTC
URL: https://dev.mysql.com/doc/refman/5.6/ja/type-conversion.html
なるほど、環境によって異なるのですね。

MySQL において、異なる型の比較は思わぬ結果になってしまう事がある、
という事で、注意しましょう。

0 件のコメント:

コメントを投稿