VB.NetのASP.NET。クエリで除算したデータがInvalidCastException
よく理解しないで既存の画面に項目追加しようとしたらワケワカンナイことになった。
構造としてはよくある(のかな?)、ORACLEからデータ引っ張ってきてaspxで一覧表示させるとかそういうやつ。
いや実際こういうふわっとした理解なんだよね。この仕事8年目なんだけどね。社内ではPGだけはできるって扱いなんだよ。不思議だね。
問題。
ORACLEから引っ張ってくる項目を増やそうとした。なので、クエリに項目を追加した。
aspx側も足して・・・xsdとか使われててよくわからなかったので、外科手術 GREPかけて直接直した。
(Visual Studioからいじろうとしたら必要なソースをプロジェクトから弾いてエラーですとか喚きだした。頭のおかしいクレーマーかよ・・・)
WITH句で除算した項目をSELECTして、画面に表示しようとしたところ・・・InvalidCastException。
なんでやねん。
検証。
足したところを適当に削ってエラー位置を特定。うん、足した奴がダメみたい。外科手術で悪影響が出たわけではないようで安堵。
いろいろと試したり別の項目を使って表示させたりしたところ、除算の結果がものすごい桁数まで保持していることがわかった。
いや、適当に切り捨ててくれていいんじゃよ・・・? などと思いつつも、この桁数はいかにも怪しい。たぶんDecimalすら突き抜けるやつだこれ。
ものは試しと、ROUNDで桁数を削ると・・・通ってしまった。
だが、エラーが呼び元で出ているせいで、どこのタイミングで型変換に失敗しているのかはわからなかった。表示するときに使ってるCDecかな? と思ったがそこを削ってもエラーになる。
どうやら、クエリの結果をVB側に渡すところ、つまりDecimal型の変数に値を入れるところがあやしそうだ。
だが、その部分は特定できないかった。単純にめぼしいところをIntegerやFloatに変えてみたりもしたがエラーにならない。こいつらは適当に丸めてくれるらしい。あるいはまだ変えるべきところがあるのかもしれない。
わからん。
対策。
VB側の修正箇所がわからないので、クエリ側でROUNDした。
教訓。
除算には気をつけよう。
補足。
ROUNDを足したせいで式がごちゃついてきたので、もう少し簡単にできないかと調べたところ、CASEでゼロ除算を弾こうとしていた部分にNULLIFという関数が代用できることを知った。
NULLIF(A, B) で、A=B の場合にNULLに変換してくれる素敵関数。
ゼロで割ると例外が起きるけど、NULLで割ると結果がNULLになるので落ちない。・・・いや待て、結果がNULLになったらダメな気がする。
追加情報:型 ‘DBNull’ から型 ‘Decimal’ への変換は無効です。
ですよねー。少数桁でオーバーフローするくらいだもんげ。