QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

VB.NET- 更新処理時SQLServerタイムアウト

VB.NETにてSQLServer2000のテーブル更新プログラムを作成しています。
作成しているのは1000万件ほどの件数があるKYKINFOテーブルに対して、
別のテーブル(dbUtmp表)の値をJIGYOCD,KYKNOをキーとして更新するプログラムです。
KYKINFOのプライマリキーはJIGYOCD,KYKNO です。
以下のようなロジックで実行してみたのですが、最後のUPDATEの部分で時間が
かかってしまい、SQLServerのタイムアウト(30秒)になってしまいます。
プライマリキーが更新用インデックスとして使用されればそれほど時間はかから
ないはずなのですが、このようなロジックでは使われないのでしょうか?
なお、dbUtmp(i)は更新するための元テーブルをビュー化したものです。
まったく違う方法でもかまいませんので解決法をご存知の方、ご教授ください。

Private dvUtmp As DataView
Private dadJgyk As New SqlDataAdapter("SELECT * FROM KYKINFO", con)
Private dstJgyk As New DataSet()

strSQL = "SELECT *" & _
" FROM KYKINFO" & _
" WHERE JIGYOCD=" & dvUtmp(i)("JIGYOCD") & _
" AND KYKNO =" & dvUtmp(i)("KYKNO")
dadJgyk.SelectCommand.CommandText = strSQL
dstJgyk = New DataSet()
dadJgyk.Fill(dstJgyk, "KYKINFO")
dtJgyk = dstJgyk.Tables("KYKINFO")
drJgyk = dtJgyk.Rows(0)

drJgyk.BeginEdit()
drJgyk("KYKNAME") = dvUtmp(i)("KYKNAME")
drJgyk.EndEdit()

dadJgyk.UpdateCommand = cbJgyk.GetUpdateCommand
dadJgyk.Update(dstJgyk, "KYKINFO")

質問者:おひろ

回答

どんなUPDATE文が投げられているか確認してみてはどうでしょうか。
SQL Serverだと確かプロファイラ(?)で要求されたSQL拾えますよね。
拾えたらそのUPDATE文の妥当性を検証してはどうでしょう。

投稿者:ぴで

編集 履歴 (0)

こんにちは。

 表に外部キーが設定されていると、その確認も行いますので、一時的に外部キーを無効にしておくというのも手です。

投稿者:Jitta

編集 履歴 (0)

ぴでさん、Jittaさん、ありがとうございます。
早速プロファイラでトレースを取ってみたところ。。。
UPDATE KYKINFO
SET KYKNM = @p1
WHERE ( (JIGYOCD = @p2) AND (KYKNO = @p3) --- ここまででよいのに
AND ((KYKNM IS NULL AND @p4 IS NULL ) OR ( KYKNM = @p5 ))
AND ((KYKTEL IS NULL AND @p6 IS NULL ) OR ( KYKTEL = @p7 )
AND ((KYKADR IS NULL AND @p8 IS NULL ) OR ( KYKADR = @p9 )
・・・・・・
と全項目が最初にSELECTした値と等しいかAND条件に入れているようなのです(--;
このテーブルが項目数が120個程度あるのですが、それらがすべてWHERE句に
入ってしまっています。
こういった仕様なのでしょうか。。。プライマリキーのみで更新できるようにする
よい手がありましたらご教授ください。

-- Jittaさん
このテーブルを更新してる間にも、別のセッションから外部キーを使って検索を
行う必要があるのです。
このセッションの中だけ外部キーを無効にする、なんてことはできないですよね?

投稿者:おひろ

編集 履歴 (0)

おひろさんの書き込み (2003-10-15 13:37) より:

早速プロファイラでトレースを取ってみたところ。。。

と全項目が最初にSELECTした値と等しいかAND条件に入れているようなのです(--;

このセッションの中だけ外部キーを無効にする、なんてことはできないですよね?

 データアダプタの構成で、UpdateやInsert文を自動で作りました?もしそうなら、それが原因です。今、試せるソリューションがないのでうろ覚えですが、データアダプタの構成ウイザードで、途中でSELECT文を作るとき、オプションで全列一致をするかどうかのチェックがあります。これはウイザードを起動すると必ずオンになっているので、毎回オフにしてやらなければなりません(ように思う)。

 確かに、複数のセッションが動いてますよね--;。失礼しました。

投稿者:Jitta

編集 履歴 (0)

JittaさんのおっしゃるとおりCommandBuilderを使用しています。
これの仕様だったんですね。
-- 宣言部
Private dadJgyk As New SqlDataAdapter("SELECT * FROM F_JGYKYK", con)
Private cbJgyk As New SqlCommandBuilder(dadJgyk)
-- UPDATE文作成部
dadJgyk.UpdateCommand = cbJgyk.GetUpdateCommand
dadJgyk.Update(dstJgyk, "F_JGYKYK")

DataAdapterでSELECT実行時に指定、ということは上記宣言時に行うということでしょうか?
これから自分でも調べてみるつもりですが、詳しいやりかたがわかりましたらご教授いただけないでしょうか。
重ね重ね申し訳ありませんが、よろしくお願いいたします。

投稿者:おひろ

編集 履歴 (0)

おひろさんの書き込み (2003-10-15 16:45) より:

DataAdapterでSELECT実行時に指定、ということは上記宣言時に行うということでしょうか?

仕様です。

MSDN:.NET Framework→.NET Frameworkを使用したプログラミング→ADO.NETを使用したデータのアクセス→.NET Frameworkデータプロバイダによるデータのアクセス→自動生成コマンド より:

更新および削除のオプティミスティック同時実行制御

UPDATE ステートメントおよび DELETE ステートメントに対する自動コマンド生成ロジックは、オプティミスティック同時実行制御に基づいています。これはつまり、編集時にレコードがロックされず、他のユーザーまたはプロセスがそのレコードをいつでも変更できることを意味します。レコードは SELECT コマンドによって返された後、UPDATE ステートメントまたは DELETE ステートメントの実行前に変更されている可能性もあるため、自動的に生成される UPDATE ステートメントまたは DELETE ステートメントには、元のすべての値を含み、データ ソースから削除されていない行だけを更新するように指定した WHERE 句が含まれます。これにより、新しいデータが上書きされるのを防ぎます。自動的に生成された更新コマンドが削除済みまたは DataSet にある元の値が含まれていない行を更新しようとすると、コマンドはどのレコードにも反映されずに、DBConcurrencyException がスローされます。

元の値とは関係なく UPDATE または DELETE を実行する場合は、DataAdapter に明示的に UpdateCommand を設定し、自動コマンド生成は行わないでください

 デザイナ上でデータアダプタの構成ウイザードにより構成する場合は、「オプティミスティック」をオフにできます。

投稿者:Jitta

編集 履歴 (0)

Jittaさんありがとうございます。
やはりできないんですね。とりあえずCommandBuilderで作成したSQL文の
WHERE句以降をロジックで置き換えることで対応しました。
これからはCommandBuilderはできるだけ使用しないようにします。
ありがとうございました。

投稿者:おひろ

編集 履歴 (0)
ウォッチ

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