none
DataSetの中身を別テーブルに保存する方法 RRS feed

  • 質問

  • おはようございます。

    上記件ですがやりたいこととして、リハビリをする人の予約と実績の管理を行う為、次のようなことを予定しています。

    1.予定テーブルからその日の利用予定者をDataSetに取得してデータグリッドに表示。2.変更や追加を行う。(削除はありません。)                           3.実績管理用のテーブルへデータを追加

    ということを考えています。

    今まではAccessを使って、ワークテーブルを使うことにより実現しておりましたが、通常DataSetを使用するとデータの取得もとへ変更などを返してしまうと言うことなので、ループでデータセット内のデータをSQL(またはストアド)を使用して、実績管理用のテーブルに追加しようと考えています。

    ただ、一括でDataSet内のデータを他のテーブルに追加する方法がないものかと考えており、質問しました。

    何か、ご助言をいただければ幸いです。

    よろしくお願いします。

    2007年2月15日 23:20

回答

  •  TI-cb400s さんからの引用

    1.テーブルAからデータを取得 → DataGridViewに表示
    2.データの更新・追加を行う。
    3.DataGridViewのデータをテーブルBに追加をする。

    という場合に、SqlCommandを使うとのことでしたが、その場合はどのように実行をするのでしょうか。


    通常は、SqlDataAdapterを使います。SqlCommandを使う場合には、SqlDataAdapterが内部でSqlCommandを使用して行っていることを、全て自分でコーディングして行わなければなければなりません。
    簡単に書けば、
    1.SqlCommandでselect文を発行し、SqlDataReaderで1レコードずつ読みながらデータテーブルに格納します。
    2.ユーザーがデータテーブルを変更し、そのデータテーブルをデータベースに書き戻します。データテーブルから追加された行を抽出し、その1行毎にUpdate文を発行します。追加された行かどうかは、DataRowのRowStateを見ればわかります。同様に、変更された行、削除された行も処理します。

    (参考)
    方法 : 変更された行をチェックする
    http://msdn2.microsoft.com/ja-jp/library/czb9z269(VS.80).aspx

    以上を一発でやってくれているのがSqlDataAdapterです。

     TI-cb400s さんからの引用

    DataGridViewへの追加・変更というのは、その都度DataSourceとなっているDataSetへも反映されるのでしょうか。

    されます。ただし、正確には内部ではもっと複雑なことが行われています。
    データテーブルの各行は複数のバージョンを持てるようになっています。このバージョンは、DataRowのDataRowVersionで判断できます。
    DataGridViewからユーザーが変更した値は、DataRowVersionがProposeの行にセットされます。ユーザーが次の行に移動すると、つまり、レコードセレクタがえんぴつマークから通常のマークに変わると、Proposeの行の値は、Currentの行の値として採用されます。さらに、DataAdapter.Update、もしくは、AcceptChangesを実行すると、Currentの行の値は、Originalの行の値として採用されます。

    (参考)
    DataRowVersion 列挙体
    http://msdn2.microsoft.com/ja-jp/library/system.data.datarowversion(VS.80).aspx

     TI-cb400s さんからの引用

    ヘルプにはAcceptChangesを実行するとDataSetへ変更が反映されるというような事があったように思えるのですが、DataAdapterのUpdateと混ざってよくわからなくなっています。

    AcceptChangesというのは、あくまでもデータテーブルに限定した話です。先に述べたように、Propose値がCurrent値として確定されることなどが起こります。
    一方、DataAdapter.Updateは、データベースのテーブルにデータを反映させる処理です。実は、DataAdapter.Updateを実行すると、自動的にAcceptChangesも実行されるようになっています。

     TI-cb400s さんからの引用

    また、DataSetのデータをループする場合(上記3を実行するには、テーブルBへの追加用のストアドプロシージャーをレコード分ループして実行する必要があるように思うのですが)どのような記述となるのでしょうか。

    これは前述したとおりです。SQL文の実行をストアドプロシージャの実行に変えるだけです。おっしゃる通り、ループして処理を行います。

     TI-cb400s さんからの引用

    DataReaderは以前、テストをしたのですがこちらの場合は、DataReader.readがTrueの場合は、データがあるのでその間は処理を続けるというようなことをしたのですが、似たような方法があるのでしょうか。

    この辺りはAccessのVBAと全く同じ考え方で、おっしゃられるようにループして処理を行います。

    2007年2月25日 13:57
    モデレータ

すべての返信

  • SelectCommandは予定テーブルから取得するようにし、変更や追加をする際に使用するUpdateCommandやInsertCommandは、実績管理用のテーブルへ保存するようなSQL文、もしくはストアドプロシージャを指定すれば良いように思います。
    2007年2月16日 0:44
    モデレータ
  • TableAdapterを利用してDataSetにFillメソッドで読み込んでいると思います。
    更新は同様にUpdateメソッドを呼ぶと思います。

    TableAdapterのFillメソッドで使うSelectCommandは一括でデータを取得します。
    ただしTableAdapterのUpdateメソッドで使うInsertCommandやUpdateCommandは一レコードづつです。

    なのでInsertCommandやUpdateCommandをストアドにしてストアドの中で予定テーブルと実績管理用のテーブルをメンテするのがいいと思います。

    一括でDataSet内のデータを他のテーブルに追加するのはレコードの構成とか変換が必要な場合がほとんどでそういうメソッドもありません。

    2007年2月16日 1:42
  • ご回答ありがとうございます。

    単純に、DataSetの内容をデータグリッドに表示することができたのですが、コントロールがすべてテキストボックスの状態の場合であり、それを実際に使うときのようにコンボボックスなどにしたいと思い、いろいろと調べてはいるのですが、そこで躓いている状態です。(Accessのイメージで簡単に考えていたのですが、全然違うので完全に手が止まっています。)

    質問の趣旨が変わってしまうのですが、こちらがわからないことには前に進めないので、少々お付き合いをお願いいたします。

    まずやりたいこととして、はじめにも書いたのですがリハビリを行う患者さんの実績管理をしたいと考えています。

    予約テーブルには、患者コード、担当者、曜日、時間という項目があり、(他にもありますが、実績管理には使いません)実施する曜日に該当するデータを取得して、担当者と時間の変更があるので、データグリッド内で変更を行い、実績テーブルに保存するという動作を考えています。

    予定にない患者さんがリハビリを行うこともあるので、患者・担当者・時間はコンボボックスで選択できるようにしたいのですが、まずここで躓いています。

    1.データグリッドにDataSourceがあるのですが、これに予約テーブルから取得してDataSetをセットすれば良いかと思ったのですが、それでは表示されなかったので、DataViewにDataSetの内容を取得してDataSourceにセットしたところ、表示はされたのですが、はじめから列を作っておくと、既存の列とは別に列ができてしまったので、既存の列にデータをセットするにはどのようにすればよいか。

    2.患者、担当者、時間はそれぞれテーブルを作成してあり、それを取得するストアドプロシージャを作成して、コンボボックスのDataSourceなどにセットをするのでしょうか。(ヘルプや本を見ているのですが、なかなかこのような具体例が欠かれているものがないものですから。)また、ストアドプロシージャでは複数のSelect文を一度に作ることができると思うのですが、このようにして、取得したデータを別々のコンボボックスのデータソースとしてセットすることができるのか。

    といったことがわかりません。

    今、ヘルプを読み直しているところなのですが、もしよろしければご教授いただけないでしょうか。

    どうか、よろしくお願いします。

    2007年2月18日 8:37
  • VisualStudioのバージョンは? 2002/2003/2005

    >データグリッドに
    DataGrid ですか? DataGridView ですか?

    >DataViewにDataSetの内容を取得してDataSourceにセット
    DataViewにDataTableの内容を取得してDataSourceにセットですよね?

    VisualStudio2005ならBindingSourceを使うと同じようなことができます。

    >はじめから列を作っておくと、既存の列とは別に列ができてしまった
    型つきDataSetを作るとデザイナーで列を作れるので便利ですよ。

    >患者、担当者、時間はそれぞれテーブルを作成してあり、それを取得するストアドプロシージャを作成して、コンボボックスのDataSourceなどにセットをするのでしょうか
    はい。

    >なかなかこのような具体例が欠かれているものがないものですから
    DataGridViewComboBoxColumn クラスのHELPは見ましたか?
    具体例は書かれていませんか?
    DataGridは標準でComboBoxはなかったと思います。

    >ストアドプロシージャでは複数のSelect文を一度に作ることができると思うのですが、このようにして、取得したデータを別々のコンボボックスのデータソースとしてセットすることができるのか
    可能ですがTableAdapterは使えません。
    初学者の方はひとつひとつやられることをお勧めします。

    2007年2月18日 9:01
  •  TI-cb400s さんからの引用

    はじめから列を作っておくと、既存の列とは別に列ができてしまったので、既存の列にデータをセットするにはどのようにすればよいか。

    この部分はAutoGenerateColumnsプロパティがTrueだからかもしれません。

    DataGridView.AutoGenerateColumns プロパティ
    http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.datagridview.autogeneratecolumns(VS.80).aspx

    2007年2月18日 11:34
    モデレータ
  • お返事が遅くなりました。

    ヘルプを読み、まず以下のように記述をして見ました。

    Dim Cn As New SqlConnection(My.MySettings.Default.Con)
            Dim Sc As New SqlCommand, Sc2 As New SqlCommand
            Dim Da As New SqlDataAdapter
            Dim Ds As New DataSet
            Dim DtView As DataView

            With Sc
                .CommandText = "sp_riha_yoyaku"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
                .Parameters.Add("@youbi", SqlDbType.TinyInt).Value = CInt(Weekday(Me.DTP_Date.Text))
            End With

            With Sc2
                .CommandText = "sp_list_ptcode"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
            End With

            Debug.WriteLine(CInt(Weekday(Me.DTP_Date.Text)))
            Da.SelectCommand = Sc

            Da.Fill(Ds, "t_yoyaku")
            Da.Fill(Ds, "t_patient")

            'DtView = New DataView(Ds.Tables("t_yoyaku"))

            Me.Dgv_Yoyaku.DataSource = Ds

            With Me.Column1
                .DataPropertyName = "ptcode"
                .DataSource = Ds.Tables(1)
                .DisplayMember = Ds.Tables(0).Columns(0).ToString
            End With

        End Sub

    これを実行するとエラーは出ないのですが、DataGridViewのComboboxが青く反転するのですが、その状態ではデータが表示されません。しかし、コンボボックスをクリックするとデータが表示されました。

    今まで理解できている流れとして、

    1.DataGridViewのDataSourceをセットする。
    2.DataGridViewのComboboxのDataPropertyName、DataSource,Displaymemberをセットする。

    ということだと思うのですが、分からない点は次のようになります。

    1.なぜ、実行するだけではデータが表示されないのか。

    2.DataPropertyNameは何の為に指定をするのか(コード上は指定をしてありますが、よく分からなかったので適当につけてあります。)

    3.Comboboxが複数ある場合、それぞれDatasourceを用意する必要があると思うのですが、ストアドプロシージャーではSelect文をいくつもひとつのストアドに含めることができるのですが、どのようにして取り出せばよいか。(DataSetに別々に指定する方法または、それ以外でDataSourceを指定できる方法があれば知りたいです。ヘルプではそのまま、Select文が記述してあったと思うのですが、なぜあのように記述ができるのかがわかりません。)それとも、私が上で記述したようにそれぞれストアドプロシージャーを用意して、DataSetにセットする必要があるのか。(ただ、この方法は私にも非効率だと思われるぐらいなので、違うとは思うのですが。)

    分からない事だらけで申し訳ありませんが、もう少しお付き合い下さい。

    trapemiya様の件はまだ調べていません。申し訳ありません。

    2007年2月20日 7:55
  • 自分なりにいろいろと試しているのですが、どうにも思ったとおりに動作しません。

    現在は、以下のように記述をしてあります。

    Dim Cn As New SqlConnection(My.MySettings.Default.Con)
            Dim Sc As New SqlCommand, Sc2 As New SqlCommand
            Dim Da1 As New SqlDataAdapter, Da2 As New SqlDataAdapter
            Dim Ds1 As New DataSet, Ds2 As New DataSet
            Dim DtView As DataView

            With Sc
                .CommandText = "sp_riha_yoyaku"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
                .Parameters.Add("@youbi", SqlDbType.TinyInt).Value = CInt(Weekday(Me.DTP_Date.Text))
            End With

            With Sc2
                .CommandText = "sp_test"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
                .Parameters.Add("@youbi", SqlDbType.TinyInt).Value = CInt(Weekday(Me.DTP_Date.Text))
            End With

            Debug.WriteLine(CInt(Weekday(Me.DTP_Date.Text)))
            Da1.SelectCommand = Sc

            Da1.Fill(Ds1)

            Da2.SelectCommand = Sc2
            Da2.Fill(Ds2)


            'DtView = New DataView(Ds.Tables("t_yoyaku"))

            Me.Dgv_Yoyaku.DataSource = Ds1

            With Me.Column1
                .DataPropertyName = "ryk_ptcode"
                .DataSource = Ds2.Tables(0)
                .DisplayMember = Ds2.Tables(0).Columns(1).ToString
                .ValueMember = Ds2.Tables(0).Columns(0).ToString
            End With

            With Me.Column2
                .DataPropertyName = "ryk_tantoucode"
                .DataSource = Ds2.Tables(1)
                .DisplayMember = Ds2.Tables(1).Columns(1).ToString
                .ValueMember = Ds2.Tables(1).Columns(0).ToString
            End With

            With Me.Column3
                .DataPropertyName = "ryk_time"
                .DataSource = Ds2.Tables(2)
                .DisplayMember = Ds2.Tables(2).Columns(1).ToString
                .ValueMember = Ds2.Tables(2).Columns(0).ToString
            End With

            With Me.Column4
                .DataPropertyName = "ryk_syubetu"
                .DataSource = Ds2.Tables(3)
                .DisplayMember = Ds2.Tables(3).Columns(1).ToString
                .ValueMember = Ds2.Tables(3).Columns(0).ToString
            End With

    ストアドプロシージャは以下のようになっています。

    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    GO
    ALTER procedure [dbo].[sp_test] (@youbi tinyint)
    AS
    /*指定した曜日で、終了していない予約情報を取得*/
    SELECT t1.pt_code,t1.pt_name FROM t_patient AS t1

    /*フォームのデータグリッドに表示するデータ用で時間と担当者を取得*/
    SELECT t2.rsf_id, t2.rsf_name FROM t_riha_staff AS t2 WHERE t2.rsf_endday IS NULL

    SELECT t3.rjt_id, t3.rjt_time FROM t_riha_time AS t3 WHERE t3.rjt_endday IS NULL

    SELECT t4.rsb_id,t4.rsb_name FROM t_riha_syubetu AS t4


    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    GO
    ALTER PROCEDURE [dbo].[sp_riha_yoyaku] (@youbi tinyint)
    AS
    SELECT t.ryk_ptcode, t.ryk_tantoucode, t.ryk_time, t.ryk_syubetu FROM t_riha_yoyaku AS t
    WHERE t.ryk_youbi=@youbi

    これを実行すると、DataGridViewの一番左の列が青く反転しており、何も表示がされません。しかし、各Comboboxには指定したテーブルのデータが格納されています。エラーは出ていません。

    これ以上どうにも分かりません。どうか、アドバイスをお願い致します。

    2007年2月23日 6:47
  • とりあえずストアドプロシージャを使わず、サンプルコードを書いてみました。

            Dim Cn As New SqlConnection(My.MySettings.Default.testdbConnectionString)

            Dim Sc As New SqlCommand, Sc2 As New SqlCommand

            Dim Da1 As New SqlDataAdapter, Da2 As New SqlDataAdapter

            Dim Ds1 As New DataSet, Ds2 As New DataSet

     

            With Sc

                .CommandText = "select TEST1, TEST2ID from test"

                .CommandType = CommandType.Text

                .Connection = Cn

            End With

     

            With Sc2

                .CommandText = "select TEST21ID from test2"

                .CommandType = CommandType.Text

                .Connection = Cn

            End With

     

            Da1.SelectCommand = Sc

            Da1.Fill(Ds1)   'デフォルトでTableというデータテーブルが作られる。

     

            Da2.SelectCommand = Sc2

            Da2.Fill(Ds2)   'デフォルトでTableというデータテーブルが作られる。

     

            Me.DataGridView1.DataSource = Ds1

            Me.DataGridView1.DataMember = "Table"

     

            '1列目 テキストボックス

            Dim column As DataGridViewColumn = New DataGridViewTextBoxColumn()

            column.DataPropertyName = "TEST1"   'この名前がデータテーブルの列名と一致している必要がある。

            column.Name = "TEST1名前'"

            DataGridView1.Columns.Add(column)

     

            '2列目 コンボボックス

            Dim columnCombo As DataGridViewComboBoxColumn = New DataGridViewComboBoxColumn

            columnCombo.DataSource = Ds2.Tables("Table")

            columnCombo.DisplayMember = "TEST21ID"

            columnCombo.DataPropertyName = "TEST2ID"    'この名前がデータテーブルの列名と一致している必要がある。

            columnCombo.Name = "TEST2ID名前'"

            DataGridView1.Columns.Add(columnCombo)

     

    #TEST2IDとTEST21IDが紛らわしくなっちゃいました。気をつけて下さい。(^^;

    2007年2月23日 10:12
    モデレータ
  • こんにちは。

    やっと、表示するところまでたどりつきました。

    ヘルプを読んでいて、DataMemberはデータソースにセットするデータセットなどのテーブルがひとつの場合は指定をしなくて言いいとあったので、そのとおりにしていたのですが、それがいけなかったようです。DataMemberを指定したら表示がされました。

    あと、trapemiyaさんがおっしゃっていた、AutoGeneratecolumnプロパティがなかなか見つからなかったのですが、ヘルプを呼んでいたら、コードで直接指定をするということがわかり、列が自動で増えてしまう点も解決しました。

    また、テストをしていてどうしてもひとつだけ列の表示の時点でエラーになってしまい悩んでいたのですが、各テーブルのフィールド型が違っていたせいとわかり(3日ほどか悩みましたが・・・)希望通りに動作しました。

    当初の質問の入り口に差し掛かることなく、ここまでかかってしまいましたが、次は、InsertCommandを作成して、テストをしてみたいと思います。

    ここまでのご報告までです。

     

    2007年2月24日 3:49
  •  TI-cb400s さんからの引用

    ヘルプを読んでいて、DataMemberはデータソースにセットするデータセットなどのテーブルがひとつの場合は指定をしなくて言いいとあったので、そのとおりにしていたのですが、それがいけなかったようです。DataMemberを指定したら表示がされました。

    確かにMSDNにはそのように書かれてますね。使用例にコードを示すと書いてあるんですが、コードも載ってないですね。MSDNが誤っているのかなぁ・・・。
    私の方でも、DataMemberを設定しないとDataGridViewにデータが表示されませんでした。

    DataGridView.DataMember プロパティ
    http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.datagridview.datamember(VS.80).aspx

    DataGridView.DataMember Property
    http://msdn2.microsoft.com/en-us/library/system.windows.forms.datagridview.datamember(VS.80).aspx

    2007年2月24日 7:47
    モデレータ
  • おはようございます。

    わざわざ、ご確認いただきましてありがとうございます。

    次はいよいよ、データグリッドビューに表示したデータを追加・変更して取得元とは違うテーブルへデータを追加したいと思い、以下のようにInsertcommandをセットしました。(データグリッドビューにデータを取得するところに追加してあります。)

            With InsCmd
                .CommandText = "sp_ins_rihajisseki"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
                .Parameters.Add("@ptcode", SqlDbType.VarChar).Value = Val(Me.dgv_cbo_ptcode.ValueMember)
                .Parameters.Add("@date", SqlDbType.SmallDateTime).Value = CDate(Me.DTP_Date.Text)
                .Parameters.Add("@tantou", SqlDbType.SmallInt).Value = Val(Me.dgv_cbo_tantou.ValueMember)
                .Parameters.Add("@syukketu", SqlDbType.TinyInt).Value = Val(Me.dgv_cbo_syukketu.ValueMember)
                .Parameters.Add("@syubetu", SqlDbType.TinyInt).Value = 1
                .Parameters.Add("@time", SqlDbType.SmallInt).Value = Val(Me.dgv_cbo_time.ValueMember)
            End With

            Da1.SelectCommand = Sc
            Da1.InsertCommand = InsCmd
            Da1.UpdateCommand = InsCmd

    次に、フォームにコマンドボタンを用意して、Updateを実行すればよいとのことなので、以下のように記述をしました。

    Try
                Da1.Update(Ds1.Tables(0))
                MsgBox("更新に成功しました。")
            Catch ex As Exception

          End Try
    (Da1とDs1はPublic Classのすぐ次に宣言してあります。)

    エラーは出ないのですが、データがテーブルに追加されません。また、データの取得もとのテーブルについても何の変化も起こっていません。

    ストアドプロシージャーは以下のようになっています。

    set ANSI_NULLS ON
    set QUOTED_IDENTIFIER ON
    GO
    ALTER PROCEDURE [dbo].[sp_ins_rihajisseki] (@ptcode varchar(10),@date smalldatetime,
    @tantou smallint,@time tinyint,@syukketu tinyint,@syubetu tinyint)
    AS
    INSERT INTO t_riha_jisseki (rjk_ptcode,rjk_date,rjk_tantouid,rjk_time,rjk_syukketu,
           rjk_syubetu)
    VALUES (@ptcode,@date,@tantou,@time,@syukketu,@syubetu)

    いったいどこがおかしいのでしょうか。

    ヘルプを見てもわからなかったのですが、データグリッドビューに一旦テーブルからデータを取得してそれを変更した場合に、先に何かしなければならない事があるのでしょうか。

    どうか、よろしくお願いします。

    2007年2月24日 22:38
  • 経過報告です。

    いろいろと調べてはいるものの、どうにもわからないことだらけです。SQLServerManagementStudioから、直接テーブルを覗いてみてもどうにもうまく確認ができなかったので、ひとつフォームを作成し、そこにDataGridViewをひとつ配置して、実績を入れるテーブルを表示させてみると、SQLServerManagementStudioでは確認ができなかったのですが、すべての項目が0となっているレコードが複数はいっていました。

    一体何が起こっているかよく理解できません。

    データベースへの登録や修正に何かタイミングのようなものがあるのでしょうか。

     

    2007年2月25日 3:03
  •  TI-cb400s さんからの引用

            With InsCmd
                .CommandText = "sp_ins_rihajisseki"
                .CommandType = CommandType.StoredProcedure
                .Connection = Cn
                .Parameters.Add("@ptcode", SqlDbType.VarChar).Value = Val(Me.dgv_cbo_ptcode.ValueMember)
                .Parameters.Add("@date", SqlDbType.SmallDateTime).Value = CDate(Me.DTP_Date.Text)
                .Parameters.Add("@tantou", SqlDbType.SmallInt).Value = Val(Me.dgv_cbo_tantou.ValueMember)
                .Parameters.Add("@syukketu", SqlDbType.TinyInt).Value = Val(Me.dgv_cbo_syukketu.ValueMember)
                .Parameters.Add("@syubetu", SqlDbType.TinyInt).Value = 1
                .Parameters.Add("@time", SqlDbType.SmallInt).Value = Val(Me.dgv_cbo_time.ValueMember)
            End With

            Da1.SelectCommand = Sc
            Da1.InsertCommand = InsCmd
            Da1.UpdateCommand = InsCmd


    この流れで来るのであれば、

    Da1.UpdateCommand.ExecuteNonQuery()

    です。ただ、一般的にはこのような場合にはSqlDataAdapterは使用せず、SqlCommandを使用します。

    SqlDataAdapterというのは、データテーブルとデータベースのテーブルとの間で、データのやり取りを行うものです。
    ユーザーがデータテーブルに変更を行った場合、その行には追加されたのか、変更されたのか、削除されたのかのマークが付きます。この状態でSqlDataAdapter.Updateを実行すると、その行に合わせてSQL文(またはストアドプロシージャ)が実行されます。つまり、追加された行に対してはInsertのSQL文が、変更された行に対してはUpdateのSQL文が、削除された行に対してはDeleteのSQL文が、データベースに対して自動的に投げられます。そのSQL文(またはストアドプロシージャ)を指定しているのが、SqlDataAdapterのInsertCommand, UpdateCommand, DeleteCommandです。

    2007年2月25日 5:02
    モデレータ
  • ご回答ありがとうございます。

    このあたりがヘルプを読んでもどうも理解できないのですが、SqlDataAdapterを使う場合と、SqlCommandを使う場合があるようなのですが、どのようにこれらは使い分けをするのでしょうか。

    また、SqlCommandを使用するというのは、SqlCommandに対して今回行ったように、ストアドプロシージャー(又はクエリ)、パラメータなどをセットして、ExecuteNonQueryを実行するというやり方をするのでしょうか。

    ちょっとよくわからなくなってしまいましたので、少し整理をしたいと思います。(Accessとはだいぶ勝手が違うので、非常に戸惑っています。)

    また、余談なのですが、ストアドプロシージャーを使う場合と、直接SQL文を記述する場合とどのように使い分けをするのでしょうか。今回行ったように、コンボボックスにセットするためのデータソースなどでそれほどデータが無い場合などは、どちらを使っても代わりが無いように思えるのですが、何か皆様は基準のようなものをもっていらっしゃるのでしょうか。

    2007年2月25日 5:58
  •  TI-cb400s さんからの引用

    このあたりがヘルプを読んでもどうも理解できないのですが、SqlDataAdapterを使う場合と、SqlCommandを使う場合があるようなのですが、どのようにこれらは使い分けをするのでしょうか。

    AccessとADO.NETの大きな違いは、Accessは接続型であり、ADO.NETは基本的に非接続型であるということです。
    Accessはテーブルにアクセスしている時、ずっとデータベース(MDB)に接続されたままになります。しかし、ADO.NETの場合は非接続が基本になります。つまり、データベースにつなぎ、データテーブルにデータを入れて、すぐにデータベースとの接続が切れます。そして、あとはデータテーブルを相手にユーザーは操作をすることになります。その後、ユーザーによって更新されたデータテーブルをデータベースのテーブルにまとめて書き戻す際に、ふたたびデータベースに接続して書き戻します。

    先ほど、ADO.NETの場合は非接続が基本だと言いました。基本ですので、実はAccessのように接続型であるSqlDataReaderというものも存在します。AccessのVBAのプログラミングに最も近いのがSqlDataReaderです。

    データテーブルへデータベースからデータを取ってくるのは、実はSqlCommandを使ってもできます。また、データベースへデータテーブルからデータを書き戻すことも、SqlCommandを使ってもできます。これらをまとめて簡単に扱えるようにしたものが、SqlDataAdapterです。SqlDataAdapterにはSelectCommandやInsertCommandなどがありますが、これらはSqlCommand型であることからも理解できると思います。
    ADO.NET2.0ではさらにSqlTableAdapterといって、SqlDataAdapterとDataTalbeをまとめて簡単に扱えるようにしたものが導入されました。

    余談が多くなりましたが、一般的には、データテーブルとデータベースのテーブル間で非接続で値をやりとりする場合はSqlDataAdapterを、一時的に値を取ってきたい場合(例えばログインのチェック)や、データベースのテーブルの値を更新したくてUpdate文などを発行するような場合は、SqlCommandを使います。

     TI-cb400s さんからの引用

    また、SqlCommandを使用するというのは、SqlCommandに対して今回行ったように、ストアドプロシージャー(又はクエリ)、パラメータなどをセットして、ExecuteNonQueryを実行するというやり方をするのでしょうか。

    そうです。データベースのテーブルに対して、SQL文やストアドプロシージャを実行する場合に用います。SqlDataAdapterもTableAdapterも内部ではSqlCommandを使っています。つまり、SqlCommandは基本なのです。
     TI-cb400s さんからの引用

    また、余談なのですが、ストアドプロシージャーを使う場合と、直接SQL文を記述する場合とどのように使い分けをするのでしょうか。

    基本的にはストアドプロシージャを使います。できるなら全てストアドプロシージャがベストです。ただ、あるID値をキーとして更新したり、ちょっとしたデータを読んでくる場合などは、必ずしも使わなくても良いのではないかと個人的には思います。ストアドプロシージャの利点は、そのパフォーマンスとSQLインジェクションを防ぐことだと思いますが、パフォーマンスがそれほど実感できるようなケースではない場合は、パラメータライズドクエリを使う場合もあります。例えば、ユーザーが入力した値ではなく、プログラムでID値をデータテーブルから取得して、そのID値を元にデータテーブルから値を取得するような場合などは、SQLインジェクションの可能性が無いので、パラメータライズドクエリを私は使ったりします。この場合、パラメータ化せずにSQL文を文字列連結でつないでも良さそうですが、私の中のルールとして、いかなる場合もパラメータ化せずにSQL文を文字列連結では作成しないようにしています。

    × sqlCommand1.CommandText  = "select  name  from  table  where  id ='" + id.Text + "'"

      sqlCommand1.CommandText = "select  name  from  table  where id=@id"

      sqlCommand1.CommandText = "select  name  from  table " +
                                                                                               "where id=@id"

    2007年2月25日 8:27
    モデレータ
  • 非常にわかりやすい説明をありがとうございました。

    少し自分の中での整理と、もう一度データベースなどを整理してやり直してみたいと思います。

    ただ、まだわからないのことがあるのですが、今回のように、

    1.テーブルAからデータを取得 → DataGridViewに表示
    2.データの更新・追加を行う。
    3.DataGridViewのデータをテーブルBに追加をする。

    という場合に、SqlCommandを使うとのことでしたが、その場合はどのように実行をするのでしょうか。

    DataGridViewへの追加・変更というのは、その都度DataSourceとなっているDataSetへも反映されるのでしょうか。ヘルプにはAcceptChangesを実行するとDataSetへ変更が反映されるというような事があったように思えるのですが、DataAdapterのUpdateと混ざってよくわからなくなっています。

    また、DataSetのデータをループする場合(上記3を実行するには、テーブルBへの追加用のストアドプロシージャーをレコード分ループして実行する必要があるように思うのですが)どのような記述となるのでしょうか。

    DataReaderは以前、テストをしたのですがこちらの場合は、DataReader.readがTrueの場合は、データがあるのでその間は処理を続けるというようなことをしたのですが、似たような方法があるのでしょうか。

    質問だらけで申し訳ありませんが、どうかよろしくお願いします。

     

    2007年2月25日 12:27
  •  TI-cb400s さんからの引用

    1.テーブルAからデータを取得 → DataGridViewに表示
    2.データの更新・追加を行う。
    3.DataGridViewのデータをテーブルBに追加をする。

    という場合に、SqlCommandを使うとのことでしたが、その場合はどのように実行をするのでしょうか。


    通常は、SqlDataAdapterを使います。SqlCommandを使う場合には、SqlDataAdapterが内部でSqlCommandを使用して行っていることを、全て自分でコーディングして行わなければなければなりません。
    簡単に書けば、
    1.SqlCommandでselect文を発行し、SqlDataReaderで1レコードずつ読みながらデータテーブルに格納します。
    2.ユーザーがデータテーブルを変更し、そのデータテーブルをデータベースに書き戻します。データテーブルから追加された行を抽出し、その1行毎にUpdate文を発行します。追加された行かどうかは、DataRowのRowStateを見ればわかります。同様に、変更された行、削除された行も処理します。

    (参考)
    方法 : 変更された行をチェックする
    http://msdn2.microsoft.com/ja-jp/library/czb9z269(VS.80).aspx

    以上を一発でやってくれているのがSqlDataAdapterです。

     TI-cb400s さんからの引用

    DataGridViewへの追加・変更というのは、その都度DataSourceとなっているDataSetへも反映されるのでしょうか。

    されます。ただし、正確には内部ではもっと複雑なことが行われています。
    データテーブルの各行は複数のバージョンを持てるようになっています。このバージョンは、DataRowのDataRowVersionで判断できます。
    DataGridViewからユーザーが変更した値は、DataRowVersionがProposeの行にセットされます。ユーザーが次の行に移動すると、つまり、レコードセレクタがえんぴつマークから通常のマークに変わると、Proposeの行の値は、Currentの行の値として採用されます。さらに、DataAdapter.Update、もしくは、AcceptChangesを実行すると、Currentの行の値は、Originalの行の値として採用されます。

    (参考)
    DataRowVersion 列挙体
    http://msdn2.microsoft.com/ja-jp/library/system.data.datarowversion(VS.80).aspx

     TI-cb400s さんからの引用

    ヘルプにはAcceptChangesを実行するとDataSetへ変更が反映されるというような事があったように思えるのですが、DataAdapterのUpdateと混ざってよくわからなくなっています。

    AcceptChangesというのは、あくまでもデータテーブルに限定した話です。先に述べたように、Propose値がCurrent値として確定されることなどが起こります。
    一方、DataAdapter.Updateは、データベースのテーブルにデータを反映させる処理です。実は、DataAdapter.Updateを実行すると、自動的にAcceptChangesも実行されるようになっています。

     TI-cb400s さんからの引用

    また、DataSetのデータをループする場合(上記3を実行するには、テーブルBへの追加用のストアドプロシージャーをレコード分ループして実行する必要があるように思うのですが)どのような記述となるのでしょうか。

    これは前述したとおりです。SQL文の実行をストアドプロシージャの実行に変えるだけです。おっしゃる通り、ループして処理を行います。

     TI-cb400s さんからの引用

    DataReaderは以前、テストをしたのですがこちらの場合は、DataReader.readがTrueの場合は、データがあるのでその間は処理を続けるというようなことをしたのですが、似たような方法があるのでしょうか。

    この辺りはAccessのVBAと全く同じ考え方で、おっしゃられるようにループして処理を行います。

    2007年2月25日 13:57
    モデレータ
  • お返事が遅くなりました。

    あれから、自分なりにいろいろと調べていたのですが、どうもクラスというものを使用したほうがよさそうなので、なかなか難しそうなのですが、せっかくなので、並行してこちらも勉強して試してみたいと思います。

    ただ、ちょっと頭の中を整理しないといけないので、今まで教えていただいたことをもとにまずは自分なりにがんばってみたいと思います。

    また、その段階でわからないことがあったら、質問させていただきたいと思いますので、その時は何卒よろしくお願い致します。

    本当にありがとうございました。

    2007年2月28日 8:04