2013/10/30

[TOSDI] MySQLのBIGINT符号なしをStringで扱う理由

Talend Open Studio for Data Integration関連エントリです。
今回はMySQLのBIGINT UNSIGNED(符号なし)の型のカラムを取得する際に数値型ではなく文字列型で扱う理由についての考察です。
併せてDATETIME(6)などのマイクロ秒を含む日付時刻型についての注意事項も記述します。

.

文中の「TOSDI」は「Talend Open Studio for Data Integration」を略して記述したものです。
文中の「BIGINT符号なし」は「BIGINT UNSIGNED」と同等です。


標準の動き

前回も少し触れましたが、スキーマ定義の際、BIGINT(符号あり)の場合はLong(数値型)ですが、BIGINT符号なしの列はStringクラスがプリセットされます。

結論から言うと、TOSDIにとある制限事項があり、その制限の中でこの挙動は妥当な挙動と言えます。

選択可能なJavaのデータ型

上記画像の画面でGUIにより以下の中から選択して変更することができます。
  • Boolean
  • Byte
  • byte[]
  • Character
  • Date
  • Double
  • Float
  • BigDecimal
  • Integer
  • Long
  • Object
  • Short
  • String
  • List


設定

この挙動は設定ファイルによりマッピングされたもの(正確にはマッピングされなかったもの)です。
具体的な設定の見方を記載すると長くなるので別の機会にしますが、簡単に言うと以下の通りです。

各データ型に対してどのクラスをプリセットするかが設定ファイルに書かれています。
ここで定義しなかった場合、デフォルトのクラスがプリセットされます。
デフォルトのクラスは標準ではStringになっています。

そして、BIGINTはLongにマッピングされ、BIGINT符号なしは未定義になっています。
この設定ファイルを編集すればデフォルトを変更することもできますが、このファイルでわざわざBIGINT符号なしが未定義→Stringになっているのには理由があり、以下私の推測を交えて記載します。


理由1 Longに収まらない

(PHP以外で)アプリケーション開発をされている方はすでにお気づきかもしれませんが、BIGINT符号なしが格納できる数値の最大値はかなり大きく、JavaのLong型では収まりません。

Javaの数値型(整数型)の最小最大値
型名最小値最大値
Short-3276832767
Integer-21474836482147483647
Long-92233720368547758089223372036854775807
BigInteger事実上制限なし

MySQLの数値型(整数型)の最小最大値
型名最小値最大値
SMALLINT-3276832767
SMALLINT UNSIGNED065535
MEDIUMINT-83886088388607
MEDIUMINT UNSIGNED016777215
INT-21474836482147483647
INT UNSIGNED04294967295
BIGINT-92233720368547758089223372036854775807
BIGINT UNSIGNED018446744073709551615
(TINYINTは省略)
MySQL Integer Types (Exact Value): http://dev.mysql.com/doc/refman/5.6/en/integer-types.html

実際に試してみましたが、スキーマ定義でBIGINT符号なしをLongに設定した場合、超える数値を取得したときに、明らかにオーバーしていれば例外が発生しますが、微妙なところだと丸められて誤った数値で取り扱われてしまうことがありました。
(Longで収まるかの判定をするときに計算を誤ってしまうためなんでしょうか。Java詳しくないのでよくわかりません。)

理由2 BigIntegerが使えない

Longより大きな数値を扱えるJavaのクラスはjava.math.BigIntegerなのですが、TOSDIで選択できる型は先に列挙したとおりで、残念ながらこれを選ぶことができません。
java.math.BigDecimalが選択できるのに。惜しい仕様ですね。これは使えるようにしてもいいと思うんですが。
(確実に9223372036854775807を超えないとわかっている場合のみLongに入れるという方法もありますが、私はそういうのは避けるべきと考えています。)

解決方法

そこで、どんな数値(の文字列)も確実に受け取れるのがStringというわけです。

Stringにした場合の課題と対応方法

Stringにしてしまうと困るのが四則演算ができなくなることです。
なので、加工の過程で数値として演算する場合は、別途java.math.BigIntegerをimportして、これに変換し、加工し、出力する必要があります。
(具体的な方法はここでは省略しますが、いずれ記事にしたいとは思います。)
また、MySQLから取り出すSQL文で加工し、演算が必要ない値にしてからStringに取り出すというのも選択肢の一つです。


マイクロ秒

これも前回触れた話ですが、MySQL5.6.4からDATETIME型にマイクロ秒まで格納できるようになりました。
これを取り扱うには注意が必要です。

Javaの仕様

Javaの言語で扱える日付時刻はミリ秒までです。
Java Date: http://docs.oracle.com/javase/jp/6/api/java/util/Date.html
(ナノ秒を取得するメソッドがあるみたいですが、これは現在時刻のみのようです)
ミリ秒よりも細かい精度の時刻をDate型で定義するとミリ秒に丸められてしまいます。
精度を維持するためにはこの場合もStringで受け取るしかありません。

また、マイクロ秒の時刻の演算は言語の仕様によりできません。
分解して計算するのも不可能ではないかもしれませんが、正直そんな構文見たくありません。
どうしても演算が必要な場合はSQL文で計算してからStringで取り出すのが妥当かなと思います。

注意点

注意しなければならないのは、DATETIME(6)のように定義した列もTOSDIはDATETIME()だと認識することです。
(TOSDI ver5.3.0で動作確認)
なのでデフォルトでは前回説明したとおり、Date型がプリセットされます。
これは手動で確実にStringに変更する必要があります。


Talend Open Studio for Data Integration関連エントリまとめ

.

0 件のコメント:

コメントを投稿