- PR -

ADO.NETで、データベースの初回更新が遅い

投稿者投稿内容
むーみん
常連さん
会議室デビュー日: 2005/06/23
投稿数: 41
投稿日時: 2008-01-30 12:23
いつもお世話になっております。

Visual Basic 2005のWindowsアプリケーションで、社員の出社時刻を登録する単体プログラムを作成しています。
データベースは、SQL Server 2005のExpressで、ローカルのデータベースです。

そのプログラムは、朝社員が出社する前には起動していて、画面に表示されている状態です。
で、社員は、自分の社員番号をバーコードリーダーから入力して、そうすると登録されるのようになっているのですが、起動後の初回更新時の遅いです(7〜10秒)。

更新には、SqlCommandでInsert文を発行して、Commandのパラメータに
更新内容をセットして、CommandのExequteNonQueryで更新しています。

原因は、接続(SqlConnection)を作成してOpenするときに時間がかかっているようでした。
それで、SqlConnectionをプログラムの起動時にOpenして、そのままにしておくようにしたら早くなりました。

ただ、基本的に接続は、使用する直前に作成して、必要なくなったらクローズして開放するのがよいと聞くので、この作り方は問題なのではないかと思います。

これを別の方法で解消することはできるのでしょうか?

なにか、方法がありましたら、どうぞよろしくお願いします。
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2008-01-30 12:34
そのプログラムは毎日、再起動してるんですか?
コネクションプーリングを使用してるなら、初回起動時にOpenしてすぐCloseでいいと思います。
むーみん
常連さん
会議室デビュー日: 2005/06/23
投稿数: 41
投稿日時: 2008-01-30 13:09
burton999さん、ありがとうございます。

はい、毎日再起動しています。

おっしゃる通りにしたら、開きっぱなしのときと応答速度は変わりませんでした。

こういう風にすればよいのですね。
どのプログラムでも、初回更新時がやたら遅かったので、
これからはそうしてみようと思います。

ありがとうございます。
むーみん
常連さん
会議室デビュー日: 2005/06/23
投稿数: 41
投稿日時: 2008-01-30 15:01
たびたびすみません。

元々投稿した問題の、更新速度は改善されたのですが、別のプログラムも同じ方法で直そうとしたら、速度が改善されませんでした。

そのプログラムというのは、データベースはSqlServer 2005で、Visual Basic 2005のWindowsアプリケーションです。
データプロバイダは、.NET Framework Data Provider for SQL Serverを使用しました。

二つのテーブルを一度に更新します。
親テーブルは一画面で1レコード、子テーブルは、1画面で複数レコード更新します。

画面上は、DataGridViewを使用して、編集や追加を行い、内部では、DataSetを利用していて、その中にリレーションを組んだ親子関係のある2つのDataTableを定義してあります。
更新には、SqlDataAdapterを使用しています。
これは、つくりは、型付のDataSetでデザイナから自動生成されるコードを元にして、更新Commandのクエリの中身などを少し編集して作りました。

で、このプログラムも、初回更新時が遅いので、画面の起動時に実際には使用しないSqlConnectionを作成して、オープンして解放しました。
接続文字列は、常に同じ文字列の変数から取得しているので、そうすれば、接続プールに入ると思ったのですが、やっぱり初回の更新時は遅いです。
とすると、意味のない処理をしているように思えます。
Insertでも、UpdateでもDeleteでも、何かしらの更新処理を一度実行すると、次からは早いのですが。。。

これは、回避できないのでしょうか。
それとも私の認識が間違っているのでしょうか。

自分でも引き続き調べてみますが、ご存知の方がいらっしゃいましたら、どうかよろしくお願いいたします。

[ メッセージ編集済み 編集者: むーみん 編集日時 2008-01-30 15:19 ]
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2008-01-30 15:46
引用:

むーみんさんの書き込み (2008-01-30 15:01) より:

二つのテーブルを一度に更新します。
親テーブルは一画面で1レコード、子テーブルは、1画面で複数レコード更新します。



1コネクションで2つのテーブルを更新しているのでしょうか?
それとも、2つの異なるコネクションを同時のOpenしてテーブルを更新していますか?
むーみん
常連さん
会議室デビュー日: 2005/06/23
投稿数: 41
投稿日時: 2008-01-30 16:14
burton999さん、たびたびありがとうございます。

親テーブル、子テーブル、それぞれ別のインスタンスのSqlConnectionです。
接続文字列の取得元のみ同じ接続文字列変数です。

説明不足ですみません。
型付のDataSetというのは、作成方法は、プロジェクトに新規の項目追加で、DataSetを選択して、そこにサーバエクスプローラからテーブルをDrag&Dropして、そうすると、その型付のDataTableとTableAdapterというものが自動生成されるので、それをデザイナファイルのコードを見て、そのTableAdapterクラスというのを元にしました。
このTebleAdapterクラスは、プライベートプロパティで、SqlDataAdapterとSqlConnectionを持っています。

また、FillやUpdateメソッドも持っています。FillやUpdate時にこのSqlDataAdapter型(?)プロパティやSqlConnection型プロパティが使用されています。

このTableAdapterクラスのUpdateメソッドは、結局SqlDataAdapterのUpdateメソッドを呼び出しています。

私は、これを利用して、画面で更新ボタンを押したときに、親のTableAdapterクラスをインスタンス化して、そのインスタンスのUpdateメソッドを呼び出して、親DataTableを渡してあげました。
その後、全く同様の手順で、子のTableAdapterインスタンスのUpdateメソッドを呼び出しました。
更新ボタン押下時の更新処理が終了したら、このTableAdapterのインスタンスは解放します。
(ので、更新ボタンを押下するたびに、SqlDataAdapterもSqlConnectionもインスタンス化されてOpenされます。
なのに、初回だけ遅いです。。。)

そのUpdateの中で、実際には、SqlDataAdapterをインスタンス化して、SqlConnectionもインスタンス化してSqlDataAdapterのUpdateメソッドを呼び出しています。
SqlConnectionのオープンは明示的に行わないので、DataAdapterがそのSqlConnectionをOpenしてくれて、Closeしてくれていると思います。

実際のコードを載せると膨大な量になってしまうので、一応説明したのですが、分かりにくいでしょうか。。


[ メッセージ編集済み 編集者: むーみん 編集日時 2008-01-30 16:20 ]
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2008-01-30 16:58
コネクションプールはDBサーバーとの物理的な接続を使いまわす技術です。
アプリケーション起動時はプールには接続は無い状態です。
起動時に接続をOpenすることで、DBサーバーと物理的な接続が確立されます。(おそらくこの処理がボトルネックになっている)
その後、すぐCloseすることで、接続がプールに戻されます。(物理的な接続は確立されたままです)
この状態では、プールには接続が1つしかない状態です。
ここである処理が接続をOpenすると、プールにある接続が再利用されます。(既に物理的な接続は確立されているので処理が速い)
さらに、別の処理で接続をOpenすると、プールに有効な接続が無いので、DBサーバーと物理的な接続を確立する必要がでてきます。
上記が原因だと思います。
対処方法としては、アプリケーション起動時に、コネクションプールに十分な接続を溜めておけばいいと思います。

コード:
//
// コネクションプールに有効な接続を3つ作成
//
connection1.Open()
connection2.Open()
connection3.Open()
connection1.Close()
connection2.Close()
connection3.Close()


かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2008-01-30 17:11
SQL Server 2000だからなのかな?
試してみたら、AdapterのConnectionはinternalなプロパティになったんだけどなぁ。

それは置いといて、データセットデザイナで親テーブルのアダプタのプロパティを表示させると、Connectionプロパティの中にModifireというプロパティがあります。

まずこれをinternal(private?)からpublicにします。
子テーブルのアダプタについても同様に処理します。

親テーブル、子テーブルのアダプタをインスタンス化する際
コード:

子テーブルAdapter.Connection = 親テーブルAdapter.Connection


と、Connectionを共有させてやってはいかがでしょうか?
こうすると、ひとつのConnectionだけのOpenとなるので、起動時にひとつ接続が開いていれば、初回の子テーブルの更新が遅くなることはないように思えます。

ちなみに、DataSetが定義されたアセンブリと呼び出しているアセンブリが同じなら、internalのままでも、Connectionを共有できると思います。
DataSetの定義はdllに呼び出しはexeというように、アセンブリが分かれる場合は、publicにする必要があるでしょう。

また、Connection共有しとかないと、ひとつのトランザクションで親・子を更新できない気がするんだけど、気のせい?

[ メッセージ編集済み 編集者: かずくん 編集日時 2008-01-30 17:14 ]

スキルアップ/キャリアアップ(JOB@IT)