Access VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


1632 / 2272 ツリー ←次へ | 前へ→

【6176】DoEventsの必要性? たな 05/9/6(火) 17:56 質問[未読]
【6180】Re:DoEventsの必要性? 小僧 05/9/6(火) 20:08 回答[未読]
【6182】Re:多分、必要 たん 05/9/7(水) 8:39 回答[未読]
【6184】Re:多分、必要 たな 05/9/7(水) 9:08 発言[未読]
【6185】Re:多分、必要 たん 05/9/7(水) 9:22 回答[未読]
【6187】Re:多分、必要 たな 05/9/7(水) 9:37 お礼[未読]
【6186】Re:多分、必要 たな 05/9/7(水) 9:31 質問[未読]
【6183】Re:DoEventsの必要性? たな 05/9/7(水) 8:49 お礼[未読]
【6188】Re:DoEventsの必要性? 小僧 05/9/7(水) 10:00 発言[未読]
【6189】Re:Executeメソッドのヘルプより たん 05/9/7(水) 10:17 回答[未読]
【6190】Re:Executeメソッドのヘルプより 小僧 05/9/7(水) 10:26 発言[未読]
【6192】Re:Executeメソッドのヘルプより たん 05/9/7(水) 11:22 回答[未読]
【6195】Re:Executeメソッドのヘルプより 小僧 05/9/7(水) 12:02 発言[未読]
【6221】Re:Executeメソッドのヘルプより たな 05/9/8(木) 8:50 お礼[未読]

【6176】DoEventsの必要性?
質問  たな  - 05/9/6(火) 17:56 -

引用なし
パスワード
   はじめまして。AccessVBA初心者です。

現在、処理速度の改善を行う為、
他者が作成したコードを解析しています。
(Excelへのインポート・エクスポートなどを行っている処理。)

その中で気になったのが、DoEventsです。

DoEventsの意味をはっきりわかっていないので、
必要性がわかりません。これはやはり絶対必要なものなのでしょうか。

以下に、解析中のソースの一部を記載します。

********************************************* 
  With rs
    Do Until .EOF
      strFileName = txtFolder & !FileName
      
      CurrentDb.Execute "DELETE * FROM wrk1;"
      DoEvents
      
      CurrentDb.Execute "DELETE * FROM wrk2;"
      DoEvents
      
      DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel8, "wrk1", strFileName, False, "", True
      DoEvents
      
      FUC_DropTable "Sheet1$_インポート エラー"
      DoEvents
      
      CurrentDb.Execute "DELETE * FROM wrk1 WHERE F1 = '2';"
      DoEvents
      
      strSQL = "SELECT F1 FROM wrk1 WHERE (F2 Is Null AND F5 Is Null);"
      Set rs2 = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot)
      
      Do Until rs2.EOF
        CurrentDb.Execute "INSERT INTO wrk3 ( AName ) VALUES ( '" & rs2!F1 & "' );"
        DoEvents
        
        strAName = rs2!F1
        rs2.MoveNext
        If rs2.EOF Then Exit Do
        DoEvents
      Loop
      
      rs2.Close
      
      strSQL = "UPDATE wrk4 AS A INNER JOIN wrk3 AS B ON A.AName = B.AName "
      strSQL = strSQL & "SET B.ACode = A.ACode;"
      
      CurrentDb.Execute strSQL
      DoEvents
      
      strSQL = "SELECT * FROM wrk1 WHERE F1 Is Null;"
      Set rs2 = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot)
      
      Do Until rs2.EOF
        For i = 0 To intMAX - 1
          If Not IsNull(rs2.Fields(i + 1)) And Len(Trim(rs2.Fields(i + 1))) <> 0 Then
            strSQL = "INSERT INTO wrk2 ( ID, ProductCode ) "
            strSQL = strSQL & "VALUES ( " & i + 1 & ", '" & rs2.Fields(i + 1) & "' );"
            
            CurrentDb.Execute strSQL
            DoEvents
          End If
          
          If i >= 4 And (IsNull(rs2.Fields(i + 1)) Or Len(Trim(rs2.Fields(i + 1))) = 0) Then Exit For
        Next i
        
        rs2.MoveNext
        If rs2.EOF Then Exit Do
        DoEvents
      Loop
*********************************************

上記は本当に一部を抜粋したものなのですが、
異様にDoEventsが多いようです。

DoEventsありのまま実行すると、処理時間が約17分。
DoEventsを全てコメントアウトすると、処理時間が約3分になりました。

今悩んでいるのは、DoEventsを消してしまって良いものか、
消しても問題がないものかの説明があまりつかないことです。

上記処理にて、DoEventsを取ってしまっても(必要な箇所があれば残す)
問題ないでしょうか。

逆にここには必要だという箇所があれば、アドバイス頂ければ幸いです。

すみませんが、回答お願い致します。

【6180】Re:DoEventsの必要性?
回答  小僧  - 05/9/6(火) 20:08 -

引用なし
パスワード
   ▼たな さん:
こんばんは。

>DoEventsの意味をはっきりわかっていないので、
>必要性がわかりません。これはやはり絶対必要なものなのでしょうか。

DoEvents はWindowsにいったん制御を返す命令です。
といっても実感が湧かないですよね。

適当にフォームを作成して、

テキストボックス:txtTest
コマンドボタン :実行

を配置。
実行ボタンのクリック時イベントに

Private Sub 実行_Click()
Dim I As Long
  For I = 1 To 1000
    'DoEvents
    Me.txtTest.Value = I
  Next
End Sub

と記述して実行…。結果を確認されたら、
今度は DoEvents のコメントを消して実行させてみて下さい。


>今悩んでいるのは、DoEventsを消してしまって良いものか、

ご提示されたコードの内容ですと必要がない様に見えますね。
通常、描画をするような処理に DoEvents を使うことが多いですね。

【6182】Re:多分、必要
回答  たん  - 05/9/7(水) 8:39 -

引用なし
パスワード
   推測の部分もありますが、多分、こんな感じ?

各Executeメソッドが非同期で実行されている可能性があり、非同期実行によるリスクを
避ける為に、DoEventsで制御していると思われます。

-----
非同期:
非同期式クエリーもサポートします。クエリーを実行するときに、ほかの操作を始める前に
クエリーの実行が終了するのを待つ必要はありません。
-----

つまり、クエリー自体の処理件数、マシンスペックやネットワーク環境等に依存する可能性も
ありますが、

> CurrentDb.Execute "DELETE * FROM wrk1;" (1)
> DoEvents
>      
> (略)
> DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel8, _
>  "wrk1", strFileName, False, "", True (2)

という場合、DoEventsが無ければ、(1)が完了する前に、(2)が非同期で起動され、
最悪の場合、(2)で転送中のデータが(1)によって削除され、(2)終了時のデータに
整合が取れなくなる可能性があるということです。

> strSQL = "UPDATE wrk4 AS A INNER JOIN wrk3 AS B ON A.AName = B.AName "
> strSQL = strSQL & "SET B.ACode = A.ACode;"
>
> CurrentDb.Execute strSQL (3)
> DoEvents
>
> strSQL = "SELECT * FROM wrk1 WHERE F1 Is Null;"
> Set rs2 = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot) (4)

また、(3)でstrSQL変数に格納たものが実行されていますが、(4)の手前で
strSQLが更新されており、(3)の実行が完了していなければ、strSQL変数の
中身は整合が取れていない事になります。

# メモリの実行上は既に(3)で処理が動いているので、処理自体には問題が発生しないと
# 思われますが、私も所詮1ユーザなので、メモリ管理が内部でどう動いているかは
# 分かりません。

解決策としては、一連の処理をトランザクション化して、各SQL実行の同期を取るのが
良いと思われますが、具体的なルーチンは勘弁。
(BeginTrans等を使います。)

各SQLは前述したように、さまざまな環境に依存しますので、今回テストした時に、
DoEventsを外して早くなったので「外してOK」という判断は早計と思われます。

前任者はそういう非同期による処理異常をふせぐ事を踏まえ、DoEventsを各処理の
間に入れていると思われます。
(トランザクション化を知らなかった可能性もありますが。)

【6183】Re:DoEventsの必要性?
お礼  たな  - 05/9/7(水) 8:49 -

引用なし
パスワード
   ▼小僧 さん:

おはようございます。

回答ありがとうございます。

早速下記のソースを試させていただきました。

DoEvents、理解できたと思います。

DoEventsがある場合には、処理が見えるんですね。
そして実行中にボタンの操作も行えるという事ですね。

DoEventsをコメントアウトした時には、
最終結果だけが表示され、ボタンが押せない状態になっていました。

今回の解析中のものは、インポートやエクスポート処理実行中に、
ボタン操作を行い他のものも同時に実行させることはないと思うので、
(確認してみます。)
DoEventsをはずして処理の改善を行えそうです。

わかりやすい例で助かりました。

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

【6184】Re:多分、必要
発言  たな  - 05/9/7(水) 9:08 -

引用なし
パスワード
   ▼たん さん:

ソースの確認までしていただき、ありがとうございます。

自分も気になっていた点は、むやみにDoEventsを入れたのではなく、
意味があって、必要だから入れているのかどうかです。

たんさんの解説をよく読ませていただき、再度検討します。

現在処理時間が遅いだけでなく、オーバーフローしてしまうらしいのです。
DoEventsだけでなく、いろいろなものが潜んでいるかも知れないので、
もう一度、よく解析してみたいと思います。

ご指導ありがとうございます。

【6185】Re:多分、必要
回答  たん  - 05/9/7(水) 9:22 -

引用なし
パスワード
   >現在処理時間が遅いだけでなく、オーバーフローしてしまうらしいのです。

オーバーフローは単に、暗黙の型指定がおかしいからでは?

例えば処理対象のExcelシートが32768行以上ある場合、Accessの整数型は
32767がMAXなので、Iが暗黙の型指定で整数型なら、1行処理する事に、
I+1だけしれてば、当然オーバーフローです。

# この辺は「オーバーフロー」等の語句で調べれば、
# 何故オーバーフローエラーが出るのかという事でヘルプや掲示板でさんざん
# 出ると思います。(私から見ればFAQのレベルと思います。)

【6186】Re:多分、必要
質問  たな  - 05/9/7(水) 9:31 -

引用なし
パスワード
   ▼たん さん:

もう一度じっくり読ませていただきました。
DoEventsというものは、ただ単に画面がフリーズしたようにならないという
ことではないんですね。

下記のソースで言いますと、

> CurrentDb.Execute "DELETE * FROM wrk1;" (1)
> DoEvents
>      
> (略)
> DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel8, _
>  "wrk1", strFileName, False, "", True (2)

(1)のあとにDoEventsがあると、(1)のDELETEが終了するまではその次に
処理が移らないということでしょうか。

逆にDoEventsを入れていないと、(1)のDELETEが終わる前に、
(2)の処理へ遷移するということになってしまうということでしょうか。

説明していただいている、非同期で実行されているかどうかにも
関わっていると思いますが、やはりDELETEが終わる前に
インポートをしてしまうのはよくないですね。

トランザクション化を考えてみます。

【6187】Re:多分、必要
お礼  たな  - 05/9/7(水) 9:37 -

引用なし
パスワード
   ▼たん さん:

オーバーフローの件ありがとうございます。

いくつかある処理の中、1つが時々オーバーフローしてしまうそうなんです。

たんさんの御指摘通りまずは型指定がどうなっているかを調べるのと、
「オーバーフロー」で検索をかけて調べてみます。

お手数掛けてすみません。

【6188】Re:DoEventsの必要性?
発言  小僧  - 05/9/7(水) 10:00 -

引用なし
パスワード
   ▼たな さん、たんさん:
おはようございます。

ツリーの関係で一番下にレスしますね。


>各Executeメソッドが非同期で実行されている可能性があり、非同期実行によるリスクを
>避ける為に、DoEventsで制御していると思われます。

たん さんの仰っている上の発言がどうも理解できません。

DAO で Execute メソッドを使用した場合に非同期になるという
ソースがどこを見てもみつからないです。
(Docmd.RunSQL なら理解できるのですが)

また DoEvents を入れる事により同期が実現されるという記事も
まったく見つかりませんでした。

ご面倒だと思われますが何かそのような情報が記載されている
サイトをご紹介頂けないでしょうか?

【6189】Re:Executeメソッドのヘルプより
回答  たん  - 05/9/7(水) 10:17 -

引用なし
パスワード
   >DAO で Execute メソッドを使用した場合に非同期になるという
>ソースがどこを見てもみつからないです。
>(Docmd.RunSQL なら理解できるのですが)
>
>ご面倒だと思われますが何かそのような情報が記載されている
>サイトをご紹介頂けないでしょうか?

Executeメソッドのヘルプ(AC97)の最後に下記のような記述があります。
----
ODBCDirect ワークスペースでは、省略可能な定数 dbRunAsync を含む場合、
クエリーを非同期に実行します。
----

質問者の環境がわかっているわけではありませんが、上記が該当する可能性が
あると考え、発言しています。

答えになってますか?

【6190】Re:Executeメソッドのヘルプより
発言  小僧  - 05/9/7(水) 10:26 -

引用なし
パスワード
   ▼たん さん:
こんにちは。

CurrntDB = Jet という事ではないんですね…。

DoEvent を入れると同期が取れるというのも今ひとつなんですが…。

【6192】Re:Executeメソッドのヘルプより
回答  たん  - 05/9/7(水) 11:22 -

引用なし
パスワード
   まず、はっきりさせておくべき事は、今回提示のルーチンを
どういう意図で前任者が作成したのかということで、
前任者が居ない(連絡が取れない等)ので、その意図は、
ルーチンから推測するしかないのです。

で、私がルーチンから判断したのは、
・Executeについては、非同期で実行されていると前任者が判断し、
                     ^^^^^^^^^^^^
 非同期でも確実に1ステップずつ終わらせるように組まないと、
 このルーチンは正常に終わらないと考えた。
 (作成した当時のマシンスペックや、ネットワーク処理速度、
  処理するデータ量の大きさ等で)

# 前任者がどの資料をみて、どういう観点でそう判断したのかは
# 私は推測しか出来ません。

・プログラム制御の場合、非同期だと、前述したように、Execute
 処理が重なり、正常な結果が返らないと判断したので、
 オペレーティングシステムに制御を渡し、1個のExecute処理が
 終わってプログラムに制御が戻り、次のExecute処理を実行する
 ようにするために、1個1個DoEventを入れた。

# 重ねて言うけど、現在がどう、ではなく、まず「前任者の意図」を
# 読取る必要があるという事。

ただ、プログラム実行環境及び、プログラムを実行するユーザの
意識が変わり、「現在の処理スピードではダメだ」と思い、
今回の質問になった。

で、実際にDoEventが必要なのかどうかについては、

・実際に「個々のExecute処理にかかる時間」を計測し、
 「全体を通しで実行した時の処理にかかる時間」と比較し、
 非同期処理による、個々の処理が重なるタイミングが発生して
 いるのかどうか?

という点を調べるのが一番良い気がしますね。
(個々の処理を計測する場合、個々の処理の間の時間が計測でき
 ないので、その辺りは計測修正値として考慮する必要が生じ
 ますが。)

今思いついているのは、だいたいこんな所。

# 私の意図は分かりますか?>小僧さん

【6195】Re:Executeメソッドのヘルプより
発言  小僧  - 05/9/7(水) 12:02 -

引用なし
パスワード
   ▼たん さん:
以下当方の見解です。

Execute メソッドについては Connection オブジェクトから使用された
形跡もなく、また全てCurrentDB に対して SQL を発行している以上、
必ず同期はとれているであろう、という事。

DoEvents については描画処理の際に一つだけ記述しても安定しない場合でも
重ねて記述すると安定する場合がある、という事もあるので
必ずしも同期が取れる訳では無い事。

以上の2点と、提示されたコードから判断すると「DoEvents」の必要性が感じられません。

># 重ねて言うけど、現在がどう、ではなく、まず「前任者の意図」を
># 読取る必要があるという事。

前任者の意図がどうれあれ、現在残っているコードから判断すれば
「DoEvents」の必要性ははっきりすると思われるのですが。


[#6182]での投稿では、質問された方が

>>(1)のあとにDoEventsがあると、(1)のDELETEが終了するまではその次に
>>処理が移らないということでしょうか。

というような疑問を感じられるような回答になってしまっていませんか?


「DoEvents」を入れたために「たまたま」安定するようなコードでは
使用される環境によって左右される事になりますよね。
そんなコードであるのであれば「DoEventsの必要性」は全くないと思われます。

※あくまでも たな さんがご提示されたコードの範囲に限りますが…。

【6221】Re:Executeメソッドのヘルプより
お礼  たな  - 05/9/8(木) 8:50 -

引用なし
パスワード
   ▼小僧 さん
 たん さん

いろいろと考えて下さりありがとうございます。

前任者の作成したものが粗悪なものである、という話から改善を
するような経緯があるので、元々のソースもあまり信じられるもの
ではないんです。

今回教えていただいたことをもとに、
環境などを調べたり、実際動かしてみて試したりして
改善案を導き出したいと思います。

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

また何か質問させていただくことはあると思いますが、
よろしくお願いします。

1632 / 2272 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
1078244
(SS)C-BOARD v3.8 is Free