@array_33

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C#でSQL実行時エラーについて

Q&A

Closed

解決したいこと

※コードの再確認は月曜のため、質問への返信が遅くなります。

C#初心者です。

C#からSQLServerを利用して結果を取得しようと考えているのですが、
途中でエラーが出てしまいます。
解決方法をご教示いただければと思います。

発生している問題・エラー

System.OverflowException: 'Conversion overflows.'
Enumeration yielded no results

該当するソースコード

public int getContributions(string strMonth, ref DBColumns.SqlContributions[] sqlContributions)
{          
    strSQLCon = ConfigurationManager.ConnectionStrings["SQLCon"].ConnectionString;
    //Data Source=DB2023;Initial Catalog=TEST;Persist Security Info=True;User ID=test_user;Password=test_user

    i = 0;
    using (SqlConnection objCon = new SqlConnection(strSQLCon))
    using (SqlCommand objCmd = objCon.CreateCommand())
    {          
        try
        {          
            objCon.Open();

            SQLContributions = "SELECT CODE, NAME, CLASS, BM_NAME, DATE, TOP_BOTTOM, PRT_NAME, SECTOR, AC_WEIGHT, AV_WEIGHT , CONTRIBUTIONS FROM CONTRIBUTIONS WHERE AC_DATE = yyyymm ORDER BY TOP_BOTTOM DESC,AC_WEIGHT";
            SQLContributions = SQLContributions.Replace("yyyymm", strMonth);    //strMonthは当月

            objCmd.CommandText = SQLContributions;
            using (SqlDataReader objReader = objCmd.ExecuteReader())
           {          
                while (objReader.Read())
               {          
                    sqlContributions[i] = new DBColumns.SqlContributions();
                    sqlContributions[i].CODE = (string)objReader.GetValue(0);          
                    sqlContributions[i].NAME = (string)objReader.GetValue(1);          
                    sqlContributions[i].CLASS = (string)objReader.GetValue(2);      
                    sqlContributions[i].BM_NAME = (string)objReader.GetValue(3);              
                    sqlContributions[i].DATE = (int)objReader.GetValue(4);                 
                    sqlContributions[i].TOP_BOTTOM = (string)objReader.GetValue(5);        
                    sqlContributions[i].PRT_NAME = (string)objReader.GetValue(6);            
                    sqlContributions[i].SECTOR = (string)objReader.GetValue(7);                
                    sqlContributions[i].AC_WEIGHT = (decimal)objReader.GetValue(8); 
                    sqlContributions[i].AV_WEIGHT = (decimal)objReader.GetValue(9);
                    sqlContributions[i].CONTRIBUTIONS = (decimal)objReader.GetValue(10);
                    //最後のobjReader.GetValue(10)でエラー。
                    //列番号ではなく列名に変更しても同様のエラー。
                    
                    i++;
                    Array.Resize(ref sqlContributions, i + 1);
                }
            }
            DataBottom = i;
        }
        catch (Exception ex)
        {          
            Console.WriteLine(ex.Message);
            throw;
        }
        finally
        {          
            objCon.Close();
        }
    }

    return DataBottom;

}

自分で試したこと

Classで生成した変数にそれぞれ入れていて、そこの型はSQLと完全に同じ型で作成しております。
元々、以下のコードで試していましたが、以下のコードの場合NULL判断のところでエラーが出ます。

if (objReader["CONTRIBUTIONS"] != DBNull.Value) {
    sqlContributions[i].CONTRIBUTIONS = (decimal)objReader[10]; 
    }

初歩的な質問で申し訳ございませんが、
探しても回答を見つけられなかったため、何卒よろしくお願いいたします。

0 likes

2Answer

DBColumns.SqlContributions が定義不明です。書いてください。

テーブル CONTRIBUTIONS の定義を書いてください。

SqlDataAdapter を使って DataSet / DataTable に SQL Server のデータを取得しないのは何故ですか? やりたいことを行うためにそれではダメなのですか? とすると何故ですか?

【追記】

上の質問に返事がないのでいろいろ不明ですが、エラーメッセージ「System.OverflowException: 'Conversion overflows.'」からすると、

SQL Server の decimal 型

decimal 型と numeric 型 (Transact-SQL)
https://learn.microsoft.com/ja-jp/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver16

と、.NET の Decimal 型

Decimal 構造体
https://learn.microsoft.com/ja-jp/dotnet/api/system.decimal?view=net-7.0

とでは扱える値の大きさが異なる (SQL Server の方が大きい) ことが原因だと思います。

//最後のobjReader.GetValue(10)でエラー。

ということですから、質問者さんの SQL Server のテーブルの CONTRIBUTIONS 列に .NET の Decimal 型で扱える値を超えるものが含まれていたため、そこで OverflowException がスローされたということでしょう。

再現例は以下の画像を見てください。.NET の Decimal で扱える正の値 79,228,162,514,264,337,593,543,950,335 を 1 超えた SQL Server の decimal 値を .NET で取得しようとして OverflowException がスローされています。

debug.jpg

なお、取得する値の小数点以下の長さも関係することに注意してください。例えば、上の画像の query を、

var query = "select cast(10000000 as decimal(38,26)) val";

としても (値は上の画像の例とは違って 10000000 でしかないことに注意)、OverflowException がスローされます。

2Like

Comments

  1. @array_33

    Questioner

    回答ありがとうございます。

    DBColumns.SqlContributionsは変数を定義するクラスで、

    DBColumnsクラス内のSqlContributionsクラスで
    変数decimal? CONTRIBUTIONSを定義しております。
    DBのCONTRIBUTIONSはdecimal(38,26)です。
    SqlDataAdapterにしない理由としては、この後EXCELに出力する際に型を特定してデータを保持しておきたい為です。

    SQL取得結果が.NETのdecimal範囲を超えていないか再度確認いたします。

  2. 上の回答の最後の方に書いたこと、取得する値の小数点以下の長さも関係することに注意してください。

    すなわち、

    var query = "select cast(10000000 as decimal(38,26)) val";
    

    としても OverflowException がスローされますが、値は上の回答の画像の例とは違って 10000000 でしかないことに注意してください。

  3. @array_33

    Questioner

    回答ありがとうございます。

    調べても答えを見つけられなかった為お伺いいたしますが、
    decimal(38,26)は全部で38桁、その内26桁は小数点以下が許容範囲→12桁まで整数が許容されるという認識でいたのですが、その認識は間違っているということですよね。
    では、どのように認識すれば良いのでしょうか。

  4. decimal(38,26)は全部で38桁、その内26桁は小数点以下が許容範囲→12桁まで整数が許容されるという認識でいたのですが、その認識は間違っているということですよね。

    それは SQL Server 側の話です。.NET の Decimal 型はそうではないです。

    .NET の Decimal 値のバイナリ表現は、1 ビットの符号、96 ビットの整数、および 96 ビット整数値における小数の位置を指定するスケールファクタで構成されています。

    なので、最大値 (MaxValue) は 2^96 - 1 = 79,228,162,514,264,337,593,543,950,335 ということになります。

    SQL Server の decimal(38,26) を表すには小数点以下 26 桁が必要です。

    上に書いた例、

    var query = "select cast(10000000 as decimal(38,26)) val";
    

    ではどういうことかと言うと、10000000 の後に 26 個の 0 を続けた整数値を表現できなければならず、.NET の Decimal のバイナリ表現の整数部 96 ビットでは表現できない (MaxValue を超える) のでオバーフローします。

    上の 10000000 を 792 および 793 に代えて試してみてください。

    79200000000000000000000000000 < MaxValue < 79300000000000000000000000000

    ということで 792 は OK ですが、793 はオバーフローするはずです。

  5. @array_33

    Questioner

    回答ありがとうございます。
    型の中でも数値を扱うものの理解が難しく…

    .NETのdecimal(38,26)の場合、整数*26桁の値がMaxValueを超えるとエラーが起きるということですね(792.3を超える数値はエラー)。

    現在実際に取得する数値が確認できないため、後日あらためて数値を確認してみます。
    ただ、792.3よりも小さい数字だったように記憶しております………

  6. .NETのdecimal(38,26)の場合

    decimal(38,26) というのは SQL Server の話です。

    .NET の Decimal 型にはそういうのは無くて、上にも書いたように、バイナリ表現は 1 ビットの符号、96 ビットの整数、および 96 ビット整数値における小数の位置を指定するスケールファクタで固定されています。

  7. @array_33

    Questioner

    .NETのdecimal型の認識が間違っていてすみません。

    SQLの型を許容できるぎりぎりのサイズに小さくしたら([numeric](30, 20))
    エラーが出ずに実行できました!

    ありがとうございました!!

  8. 質問の最後の方に書いてあった NULL 対応は、また別の話なので、NULL がある場合は注意してください。

  9. @array_33

    Questioner

    そちらは問題なさそうでした。
    ご丁寧にありがとうございました。

  10. 今頃になってなんですが、Microsoft.Data.SqlClient では実装が変わったようで、

    var query = "select cast(10000000 as decimal(38,26)) val";
    

    ではオバーフローしなくなっています。

    なので、System.Data.SqlClient ではなくて Microsoft.Data.SqlClient を使うのが良さそうです。

    なお、MaxValue は Microsoft.Data.SqlClient でも同じで、

    var query = "select cast(79228162514264337593543950336 as decimal(38,0)) val";
    

    でオーバーフローするのは変わりません。

  11. @array_33

    Questioner

    ありがとうございます。

以下のコードの場合NULL判断のところでエラーが出ます。

何の例外が発生しているのでしょうか?
そこで例外が発生していることが自明であるのであればエラーメッセージやスタックトレースが確認出来るはずです。
大体の場合はそのエラーメッセージなどから原因がわかります。

0Like

Comments

  1. @array_33

    Questioner

    回答ありがとうございます。

    発生している問題・エラーに記載しておりますが、
    System.OverflowException: 'Conversion overflows.'
    というエラーが出ており、詳細を細かく見ると以下が出ます。
    Enumeration yielded no results

Your answer might help someone💌