none
C#で記述したアプリから DBにある任意のレコード・カラムのデータを思ったように更新する方法を教えてください。 RRS feed

  • 質問

  • こんばんわ!

    度々、お世話になります。
    またまた質問ですが、皆さんどうぞ宜しくお願いいたします。

    早速、本題に入らせて頂きます。
    SQL Serer Management Studioにて下記SQLコマンドを使い、希望のレコードの指定したカラムの値を任意にセットできる事を
    確認しています。

        update テーブル名 set カラム名1 = 値1 where カラム名2 = 値2


    同様の動作を C#で記述したアプリから行おうと以下のコードを試してみました。

        private void button1_Click(object sender, EventArgs e)
            {
                int founfIndex = テーブル名BindingSource.Find("カラム名2", "値2");
                テーブル名BindingSource.Position = founfIndex;
                textBox1.Text = "2000/01/01";
                テーブル名BindingSource.EndEdit();
                テーブル名TableAdapter.Update(sampleDataSet.テーブル名);
            }

          ここで、textBox1 のプロパティーで DataBindingのTextは【テーブル名BindingSource - カラム名2】に設定しています。

    しかし、思った通りにデータを更新することができません。
    そもそも根本的に考え方が誤っており、やっている事に無理があるのでしょうか?
    問題点を探そうと試みますが、動きをトレースする力もなく、問題を解決できません。

    「これをやるには、こんな方法が用意されていますよ」とか「この辺りに問題が有るのでこんな事を調べてみたら・・」みたいなアドバイス
    を頂ければと思っています。
    勿論、「ここが間違っています。こんな方法を試してみたら・・」も歓迎です。

    皆様、どうぞよろしくお願い致します。

    2010年2月15日 16:03

回答

  • コードをぱっと見た限りでは考え方は間違っていないと思いますし、うまく動作するのではないかと思われます。
    textBox1.Textに検索された結果が表示されているのであれば、BindingSourceは正しく動いています。更新されないのであればTableAdapterのUpdateメソッドが生成されていないのかもしれませんし、Updateメソッドで発行されるSQL文に誤りがあるのかもしれません。
    何かエラーは出ていますか?

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 0:48
    モデレータ
  •             int founfIndex = テーブル名BindingSource.Find("カラム名2", "値2");
                テーブル名BindingSource.Position = founfIndex;

            ① ここで、textBox1.Text に表示される値を見ると、DBに書かれている値が見えます。
               (MessageBox.Show(textBox1.Text); で見ました)
    MessageBox.Show(textBox1.Text); で表示されてもわかりますが、textBox1.Text = "2000/01/01";の行にブレークポイントをセットして実行を一時的に止め、マウスポインタをtextBox1.TextのTextに持っていくと、textBox1.Textの内容を見ることができます。DBに書かれている値とは値2でしょうか? 値2でなければうまくFindされていません。
    なおデバッグはプログラミングにおいて非常に大事なので、これ以外にもいろいろな機能があり、Visual Studioの進化と共に進化を続けています。

                textBox1.Text = "2000/01/01";      ← ②
                テーブル名BindingSource.EndEdit();
                テーブル名TableAdapter.Update(sampleDataSet.テーブル名);
            }

        ①を確認した後、【OK】をクリックすると、フォームにある textBox1.Text が②でセットしたものに
        なっていません。
    上のブレークポイントで止まっている状態からF11キーを押すと②の行が1行実行されて止まります。この時、textBox1.Textは"2000/01/01"になっているはずです。ちなみに、②でセットしたものになっていませんと書かれていますが、どのような値になっているのでしょうか? ここが一番おかしく思います。

    あと、TableAdapter プロパティーの UpdateCommand の CommandText には自動で作られた SQL文らしきものがセットされています。
    ここは結構重要です。SQL文らしきものとはどのようなものでしょうか? パラメータや場合によっては同時実行制御が入っていますから、それがSQL文らしきものに見えているのかもしれません。

    さて、こちらで簡単なテストしてみました。掲載されたコードで問題なく更新されました。なお、ひょっとしてと思い、integer型の列に文字列を入れて更新してみましたが、特にエラーにならず、更新されないだけでした。これはこれで問題だと思いましたが、いろいろいじっているのでひょっとすると私の環境だけかもしれません・・・

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 3:04
    モデレータ
  • SQL Serverは既定で自動コミットです。すなわち、Update文を実行すれば自動でトランザクションが始まり、Update文の実行が終われば自動的にコミットされてトランザクションが終了します。

    トランザクションの自動コミット
    http://msdn.microsoft.com/ja-jp/library/ms187878.aspx


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 4:23
    モデレータ
  •      textBox【x】.Text = "2000/01/01";

    設定したTextBox1 と異なる textBox【x】に更新データをセットしていました。
    ここを修正して、問題は解決しました。 お恥ずかしい、次第です。
    了解しました。ご報告いただき、ありがとうございます。

    解決できたところで根本的な問題に戻りますが、

    update テーブル名 set カラム名1 = 値1 where カラム名2 = 値2

    と同様なことを実現されたいと一番最初に書かれていましたが、そうであれば一番素直に実現する方法はSqlCommandを使うことです。
    以下にサンプルがあります。

    SqlCommand.Parameters プロパティ
    http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlcommand.parameters.aspx

    念のために書かせていただきました。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 4:37
    モデレータ
  •     ① DBへの接続のプロパティーにある『接続文字列』を持ってきましたが、書式が旨く合わないようです。
          全体を 【"】で囲む必要あり、かとも考えましたが、文字列の中に【"】が存在するし、どうして良いのか分かりません。
    その接続文字列で構いません。文字列の中に【"】が存在する場合は先頭に@を付け、【"】を2回続けます。

    @"""hoge"""

    また、プロジェクトのプロパティの「設定」に接続文字が定義してあるのであれば、以下のようにしてそれを使うことができます。

     プロジェクト名.Properties.Settings.Default.プロジェクトのプロパティの「設定」に定義してある接続文字の名前

    つまり、

    using (SqlConnection connection = new SqlConnection(プロジェクト名.Properties.Settings.Default.プロジェクトのプロパティの「設定」に定義してある接続文字の名前)) 

    です。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 6:45
    モデレータ
  • SqlCommandのConnectionプロパティが設定されていないのです。
    私が紹介したサンプルコードがあるページですと、

    SqlCommand command = new SqlCommand(commandText, connection);

    と書かれており、SqlCommandのコンストラクタでConnectionプロパティを設定しています。もちろん、以下のように後から設定してもかまいません。

    SqlCommand.Connection = connection;

    SqlCommandクラスはConnectionプロパティに設定された接続に対して、SQL文を発行します。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 8:12
    モデレータ

すべての返信

  • コードをぱっと見た限りでは考え方は間違っていないと思いますし、うまく動作するのではないかと思われます。
    textBox1.Textに検索された結果が表示されているのであれば、BindingSourceは正しく動いています。更新されないのであればTableAdapterのUpdateメソッドが生成されていないのかもしれませんし、Updateメソッドで発行されるSQL文に誤りがあるのかもしれません。
    何かエラーは出ていますか?

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 0:48
    モデレータ
  • trapemiya さん、早速ありがとうございます。

    以下のような方法で試した結果をご報告します。

    private void button1_Click(object sender, EventArgs e)
            {
                int founfIndex = テーブル名BindingSource.Find("カラム名2", "値2");
                テーブル名BindingSource.Position = founfIndex;

            ① ここで、textBox1.Text に表示される値を見ると、DBに書かれている値が見えます。
               (MessageBox.Show(textBox1.Text); で見ました)

                textBox1.Text = "2000/01/01";      ← ②
                テーブル名BindingSource.EndEdit();
                テーブル名TableAdapter.Update(sampleDataSet.テーブル名);
            }

        ①を確認した後、【OK】をクリックすると、フォームにある textBox1.Text が②でセットしたものに
        なっていません。

    あと、TableAdapter プロパティーの UpdateCommand の CommandText には自動で作られた SQL文らしきものがセットされています。

    更新データのセットそのものが旨く行っていない様な感じがします。
    コンパイル時にエラーは出ず、Button1 をクリックしても何事も無かったように通過します。

    いま、私に分かる事は、以上です。
    他に確認する必要がある事をご教授ください。

    よろしくお願いいたします。
    2010年2月16日 1:33
  •             int founfIndex = テーブル名BindingSource.Find("カラム名2", "値2");
                テーブル名BindingSource.Position = founfIndex;

            ① ここで、textBox1.Text に表示される値を見ると、DBに書かれている値が見えます。
               (MessageBox.Show(textBox1.Text); で見ました)
    MessageBox.Show(textBox1.Text); で表示されてもわかりますが、textBox1.Text = "2000/01/01";の行にブレークポイントをセットして実行を一時的に止め、マウスポインタをtextBox1.TextのTextに持っていくと、textBox1.Textの内容を見ることができます。DBに書かれている値とは値2でしょうか? 値2でなければうまくFindされていません。
    なおデバッグはプログラミングにおいて非常に大事なので、これ以外にもいろいろな機能があり、Visual Studioの進化と共に進化を続けています。

                textBox1.Text = "2000/01/01";      ← ②
                テーブル名BindingSource.EndEdit();
                テーブル名TableAdapter.Update(sampleDataSet.テーブル名);
            }

        ①を確認した後、【OK】をクリックすると、フォームにある textBox1.Text が②でセットしたものに
        なっていません。
    上のブレークポイントで止まっている状態からF11キーを押すと②の行が1行実行されて止まります。この時、textBox1.Textは"2000/01/01"になっているはずです。ちなみに、②でセットしたものになっていませんと書かれていますが、どのような値になっているのでしょうか? ここが一番おかしく思います。

    あと、TableAdapter プロパティーの UpdateCommand の CommandText には自動で作られた SQL文らしきものがセットされています。
    ここは結構重要です。SQL文らしきものとはどのようなものでしょうか? パラメータや場合によっては同時実行制御が入っていますから、それがSQL文らしきものに見えているのかもしれません。

    さて、こちらで簡単なテストしてみました。掲載されたコードで問題なく更新されました。なお、ひょっとしてと思い、integer型の列に文字列を入れて更新してみましたが、特にエラーにならず、更新されないだけでした。これはこれで問題だと思いましたが、いろいろいじっているのでひょっとすると私の環境だけかもしれません・・・

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 3:04
    モデレータ
  • trapemiya さん、何度もありがとうございます。

    > DBに書かれている値とは値2でしょうか? 

    その通りです。 Find する所までは、旨く行っていると思います。
    これまでも、デバッガーを使いたいと思いながら、簡易な方法でミスを見つける事ができていたため、
    なかなかデバッガーの使用に踏み切れませんでした。
    強力なツールだと言う事は認識しており、使い方の手引きも書いた頂いたこの機会にチャレンジして
    みようと思います。

    > でセットしたものになっていませんと書かれていますが、どのような値になっているのでしょうか?

    Button1 を押す事により Findの結果に従って、Position で指定したレコードに書き換わり、そのまま
    の状態が続きます。

    仰る通り、ここに問題があるように思います。

    SQL文については、

        UPDATE テーブル名
        SET  カラム名1 = @カラム名, ・・・・
        WHERE  ・・・・                          と書かれています。

    > パラメータや場合によっては同時実行制御が入っていますから、・・・

    詳しい事は十分理解できていませんが、まずは更新データが正しく書かれていない可能性の方が強い
    ように思われるので、そちらを調べるのが先かな・・・、と思っています。

    デバッガーの使い方を学びながら色々と調べてみたいと思います。
    状況が分かれば、また質問をアップしますので、その節にはどうぞよろしくお願いいたします。

    それから、わざわざテストまでして頂き、恐縮です。
    ありがとう御座いました。

    2010年2月16日 3:34
  • trapemiya さん、申し訳ありません。
    私の勘違い(ミス)で、問題が生じていました。

    > textBox1 のプロパティーで DataBindingのTextは【テーブル名BindingSource - カラム名2】に設定しています。

    と書きましたが、

         textBox【x】.Text = "2000/01/01";

    設定したTextBox1 と異なる textBox【x】に更新データをセットしていました。
    ここを修正して、問題は解決しました。 お恥ずかしい、次第です。
    大変お騒がせし、また貴重な時間を割いて頂き恐縮しています。


    と言う事で、厚かましくもこの場をお借りしてもう一つSQL文について質問させてください。

    参考書によると、UPDATE・・・ SET・・・ WHERE・・・ で更新した後、更新後にCOMMIT文による操作結果の確定が必要と
    書かれていますが、これをい行わなくてもDBが更新されています。

    C#アプリから行う場合には、C#が旨く面倒を見てくれているのかな・・ とも考えますが、SQL Server Management Studio
    でコマンドを実行しても、同様にCOMMIT文を実行しなくてもDBが更新されています。

    これは、たまたま旨く行くと言う事なのでしょうか。

    また、C#で記述する場合、

        テーブル名TableAdapter.Update(sampleDataSet.テーブル名);

    だけで十分なのでしょうか。
    ご教授くださいますよう、よろしくお願いいたします。
    2010年2月16日 4:09
  • SQL Serverは既定で自動コミットです。すなわち、Update文を実行すれば自動でトランザクションが始まり、Update文の実行が終われば自動的にコミットされてトランザクションが終了します。

    トランザクションの自動コミット
    http://msdn.microsoft.com/ja-jp/library/ms187878.aspx


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 4:23
    モデレータ
  •      textBox【x】.Text = "2000/01/01";

    設定したTextBox1 と異なる textBox【x】に更新データをセットしていました。
    ここを修正して、問題は解決しました。 お恥ずかしい、次第です。
    了解しました。ご報告いただき、ありがとうございます。

    解決できたところで根本的な問題に戻りますが、

    update テーブル名 set カラム名1 = 値1 where カラム名2 = 値2

    と同様なことを実現されたいと一番最初に書かれていましたが、そうであれば一番素直に実現する方法はSqlCommandを使うことです。
    以下にサンプルがあります。

    SqlCommand.Parameters プロパティ
    http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlcommand.parameters.aspx

    念のために書かせていただきました。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 4:37
    モデレータ
  • trapemiya さん、色々とご親切に、ありがとうございます。

    教えて頂いた、SqlCommandを使ったやり方を、サンプルを真似て試してみましたが、よく分からない点が幾つかあります。

           private void button2_Click(object sender, EventArgs e)
            {
                string commandText = "UPDATE テーブル名 SET カラム名1 = @demographics WHERE カラム名2 = @ID;";

            //  using (SqlConnection connection = new SqlConnection(connectionString))   ← ①
                {
                    SqlCommand cmd = new SqlCommand(commandText);

                    cmd.Parameters.Add("@ID", SqlDbType.NVarChar);
                    cmd.Parameters["@ID"].Value = "6335";
                    cmd.Parameters.AddWithValue("@demographics", "test test");

                    try
                    {
            //          connection.Open();  ← ②
                        Int32 rowsAttected = cmd.ExecuteNonQuery();
                        MessageBox.Show(rowsAttected.ToString());
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
            }

        ① DBへの接続のプロパティーにある『接続文字列』を持ってきましたが、書式が旨く合わないようです。
          全体を 【"】で囲む必要あり、かとも考えましたが、文字列の中に【"】が存在するし、どうして良いのか分かりません。

        ② 上記の事もあり、またDBと接続しているアプリの中に、テスト的に試しており ①②共にコメント・アウトして実行して
          みると、『ExecuteNonQuery:Connection プロパティーは初期化されていません。』とエラーになります。

    この辺りに付いて教えていただけると有難いのですが、どうかよろしくお願いいたします。

    2010年2月16日 6:34
  •     ① DBへの接続のプロパティーにある『接続文字列』を持ってきましたが、書式が旨く合わないようです。
          全体を 【"】で囲む必要あり、かとも考えましたが、文字列の中に【"】が存在するし、どうして良いのか分かりません。
    その接続文字列で構いません。文字列の中に【"】が存在する場合は先頭に@を付け、【"】を2回続けます。

    @"""hoge"""

    また、プロジェクトのプロパティの「設定」に接続文字が定義してあるのであれば、以下のようにしてそれを使うことができます。

     プロジェクト名.Properties.Settings.Default.プロジェクトのプロパティの「設定」に定義してある接続文字の名前

    つまり、

    using (SqlConnection connection = new SqlConnection(プロジェクト名.Properties.Settings.Default.プロジェクトのプロパティの「設定」に定義してある接続文字の名前)) 

    です。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 6:45
    モデレータ
  • trapemiya さん、ほんとに何度も申し訳ありません。

    ここまで来たら、何とかスマートな方法で書きたいと思いますので、ご迷惑かもしれませんが、もう少しお付き合いをい願いします。


    教えて頂いた二番目の方法でやってみました。


     using (SqlConnection connection = new SqlConnection(WindowsFormsApplication1.Properties.Settings.Default.sampleConnectionString)) 

    と言う感じになりました。

    connection.Open(); も生かして、エラーなくコンパイルを終わり実行しましたが、以前と同様に

    『ExecuteNonQuery:Connection プロパティーは初期化されていません。』とエラーになります。

    プロパティーの初期化とは、何をすればよろしいのでしょうか?

    時間に余裕があるときで結構ですから、また教えてください。
    よろしくお願いいたします。
    2010年2月16日 7:14
  • SqlCommandのConnectionプロパティが設定されていないのです。
    私が紹介したサンプルコードがあるページですと、

    SqlCommand command = new SqlCommand(commandText, connection);

    と書かれており、SqlCommandのコンストラクタでConnectionプロパティを設定しています。もちろん、以下のように後から設定してもかまいません。

    SqlCommand.Connection = connection;

    SqlCommandクラスはConnectionプロパティに設定された接続に対して、SQL文を発行します。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2010年2月16日 8:12
    モデレータ
  • trapemiya さん、何度もありがとうございます。

    最初、接続文字列の扱い方がわからないため、SqlCommandの第二引数を
    端折っていたのを失念していました。

    設定して確かめたところ、旨くDBが更新されるのを確認できました。

    今のところサンプルを真似て、ひとつのパターンで更新ができるのを確認しただけですが、
    この後、データ型が異なる各列に付いても色々と試してみます。

    SqlCommand を学習するきっかけを頂き、本当にありがとうございました。
    今回の例で、SqlCommand によるDB更新の全体の流れが見えてきたので、更に知識を
    習得するためには、どの辺りを調べていけば良いのか、ある程度糸口がつかめました。

    今後、 cmd.Parameters.
                    ↑ このポツ以降の持つ意味などの詳細に付いて理解を深め、

    ながら、SqlCommand が使いこなせるよう努力したいと思っています。

    今日は、長い間お付き合いいただきありがとうございました。

    また何処かで突き当たったときには、質問を投稿する事になると思いますが、その節には
    どうぞよろしくお願いいたします。
    2010年2月16日 9:49