QA@IT

SQLserverから取得した値を配列に格納し、関数の戻り値にしたいです。

17764 PV

※Visual Studio 2013、SQL server 2012を使用。OSはWindows7です。

はじめてこちらを利用します。プログラミング初級者です。
記述不足等ありましたら申し訳ありません、ご指摘ください。
以下のようなテーブルから取得した値を関数側で配列に格納し、その配列を戻り値として返したいです。

tbl_valueTest.JPG

取得する値はvalueA から valueFまでの数値のみ(該当フィールドのデータ型はいずれもdecimal(3,2))で、
ソース上は確認用にラベルコントロールに値を表示させるよう記述していますが、
本来メイン部分では別の値との相関係数算出に使用するつもりです。

以下、書籍やネット上の情報を参考に見よう見まねで記述したソースコードです。

※実行結果はいずれも 0 と表示されていたため、
Catch ex As Exception 部分の内容を確認すると、
「型 'System.Decimal' のオブジェクトを型 'System.String' にキャストできません」
と表示されていました。

テーブルのデータ型の問題なんでしょうか。

また、関数定義と呼び出し、また取得した値を配列に読み込む処理の記述について不安があるため、
誤りほかご指摘いただきたいです。
どうぞよろしくお願いします。


Imports System.Data.SqlClient

Partial Class Value_get
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load

        'SQLserverから値を取得する関数呼び出し
        Dim values() As Double = Getvalues()

        valueA_Lbl.Text = values(0)
        valueB_Lbl.Text = values(1)
        valueC_Lbl.Text = values(2)
        valueD_Lbl.Text = values(3)
        valueE_Lbl.Text = values(4)
        valueF_Lbl.Text = values(5)

    End Sub

    'データベースから値を取得する関数
    Private Function Getvalues() As Double()

        Dim result(5) As Double

        Dim cnStr As String = System.Configuration.ConfigurationManager.
            ConnectionStrings("○○○○○○ConnectionString").ConnectionString

        Using connection As New SqlConnection(cnStr)

            Dim SQL As String
'特定の「FacilityID」の日付が最新のレコードを取得したいです(改善部分)
            SQL = "SELECT valueA, valueB, valueC, valueD, valueE, valueF" &
                 " from tbl_valueTest as A" &
                 " WHERE Exists(SELECT * from tbl_valueTest" &
                 " group by FacilityID" &
                 " having FacilityID = A.FacilityID and MAX(date) = A.date)" &
                 " and FacilityID = 1"

            Dim command As New SqlCommand(SQL, connection)

            Try
                connection.Open()
                Dim dr As SqlDataReader = command.ExecuteReader()

---値を配列に読み込む
                Dim Count As Integer = 0
                Dim valuelist(5) As Double
                While (dr.Read())
                    valuelist(Count) = dr.GetString(Count)
                    Count = Count + 1
                End While

                result = valuelist
---
                connection.Close()

            Catch ex As Exception

            End Try

        End Using

        Return result

    End Function
End Class

  • 配列でないとダメなのでしょうか? 普通は DataTable に読み込んでそれを返すようにすると思うのですが。 -

回答

DataReaderのGetStringとは、引数に指定した列番号(0から始まる)の値を、DBのテーブルで文字列型の列だと仮定して取得するメソッドです。
本当の値がなんであれ、文字として取りにいきます(ToStringの様な文字化とは違います)。

しかし、実際にはSQL ServerのDecimal型だったので、文字としてみなせないものだとエラーが出ています。(エラーをみるに)。
Decimal型として取り出すか、Doubleに変換する(Double以外の数値であるため、桁が足りればDoubleに変換できます)
SQL Serverの型が.NET上なにになるかは、https://msdn.microsoft.com/ja-jp/library/cc716729(v=vs.110).aspx を参照してみてください。

(しかし、VB.NETだとGetStringをDoubleにいれようとしてるのコンパイルエラーにならないんですね)

それとは別に、問題があって。
selectの列数から、 5列分の値を取り出そうとしているように見えますが、画像の結果から現在の条件だと 2件とれるように見えます。10個の値を取ろうとすることになるのに、配列が 5つしかありません。
とりあえず、1行目だけ取り出すようにしてみます。

SQLのDecimalが .NET側でどの型になるか覚えてないですが以下の様にしてみてください。

            Try
                connection.Open()
                Dim dr As SqlDataReader = command.ExecuteReader()

---値を配列に読み込む
                Dim Count As Integer = 0
                Dim valuelist(5) As Double
                While (dr.Read())
                    valuelist(0) = Convert.ToDouble(dr(Count)) ' 変数で列インデックス指定
                    valuelist(1) = Convert.ToDouble(dr("valueB")) ' selectの列名指定
                    valuelist(2) = Convert.ToDouble(dr("valueC"))
                    valuelist(3) = Convert.ToDouble(dr(3))     ' 列インデックス指定
                    valuelist(4) = Convert.ToDouble(dr("valueE"))
                    valuelist(5) = Convert.ToDouble(dr("valueF"))
                    Exit While ' 1行分しかvaluelistのサイズが無いので。
                End While

                result = valuelist
---
                connection.Close()

            Catch ex As Exception

            End Try

drに対して文字をつかったり、変数を使ったり、数字を直接指定したりしてますが、いずれも取り出したい列を指定しています。いくつかのバリエーションでやってみました。実際はどれか1つのやり方でかまいません。
コンパイルしてないので、何かミスがあった場合はすいません。

他の指摘としては、

  • 配列よりもnew List(Of Double) と、リストに乗り換えた方がいいでしょう。
  • Usingブロックを使っているので、connection.Closeは.NETに任せても大丈夫です。
  • SqlDataReader など、ほかにもUsingブロックが使えるものがあります。
編集 履歴 (0)
  • 丁寧にご回答いただきありがとうございます。
    返信が遅れてしまって申し訳ありません。

    ちょうどご指摘いただいていたデータ型の件や、読み込み部分の改善を行なっていたところ結果が正しく得られるようになりました。
    他指摘いただいたことについてもソース改善に利用させていただきます。
    ありがとうございました!
    -

今頃気づいたのですが、アップされていたコードの Inherits System.Web.UI.Page から見ると ASP.NET Web Forms アプリですよね?

であれば、以下のようにしてデータベースのデータを格納した DataView が取得できます。配列にデータを格納するよりこちらの方がよほど簡単かつ実用的と思います。

検討してみてください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        System.Data.DataView dataview = 
            (System.Data.DataView)SqlDataSource1.Select(DataSourceSelectArguments.Empty); 
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
        ConnectionString="<%$ ConnectionStrings:○○○○○○ConnectionString %>" 
        SelectCommand="・・・SELECT クエリ(省略)・・・">
    </asp:SqlDataSource>
    </form>
</body>
</html>
編集 履歴 (0)
  • ご回答いただきありがとうございます。
    返信が遅れてしまって申し訳ありません。
    おっしゃるとおり、ASP.NET Web Forms アプリですね。

    ソース上では単純になるようラベルコントロールに表示するよう記述していますが、本当は取得した値を用いて算術を行なうので、SqlDataSourse やDataViewを用いてでないやり方を考えていました。
    -
  • 「算術を行う」際に、取得した値を配列から取り出すか DataView から取り出すかの違いが有るだけで、どっちから取り出しても算術はできると思いますが? -
  • ひょっとして DataView と GridView を混同していませんか? 全く違うものですよ。レスをよく読んで理解した上で返答いただいているでしょうか?
    https://msdn.microsoft.com/ja-jp/library/system.data.dataview(v=vs.110).aspx
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。