- ベストアンサー
EXCELマクロの処理時間を短縮したい
- Windows XP ProとEXCEL 2002を使用している状況で、EXCELマクロの処理時間が長くなって困っています。具体的な作業内容はOLEDBを使って他のDBからデータを展開し、そのデータを一覧に整理するというものです。処理に時間がかかる要因は、シートAのデータ件数が約4万件であり、シートMのデータ件数も約3万件あるためです。これらの処理をより効率的に実行する方法を教えてください。
- マクロのソースコードでは、収集したデータをシートAとシートMに展開しています。シートAのデータは番号、コード、数量、単価、追加数量、追加単価の情報を持っています。シートMのデータはコードと名称の情報を持っています。これらのデータを基本番号別に整理し、金額を計算してシート一覧に表示するという処理です。
- 具体的なマクロソースコードは、シートAのデータを一時的に読み込み、基本番号と枝番を考慮して一覧にコピーしている部分と、シートMのデータを基本番号に紐づけて名称を一覧に入力している部分です。マクロソースコードによる処理時間の短縮が望まれます。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
>Do While jdata.Cells(i, 1).Value < 9500000 > read_no = jdata.Cells(i, 1).Value / 10 > j = 0 > If jdata.Cells(i, 2).Value <> 0 Then '枝番有 > j = judata.Cells(i, 2) > End If > i = i + j > jlist.Cells(k, 1).Value = Format(read_no, "000000") > jlist.Cells(k, 2).Value = jdata.Cells(i, 3).Value > jlist.Cells(k, 4).Value = jdata.Cells(i, 4).Value > jlist.Cells(k, 5).Value = jdata.Cells(i, 5).Value > jlist.Cells(k, 6).Value = jdata.Cells(i, 6).Value > jlist.Cells(k, 7).Value = jdata.Cells(i, 7).Value > jlist.Cells(k, 8).Value = _ > Application.RoundDown((jdata.Cells(i, 4).Value * jdata.Cells(i, 5).Value + _ > jdata.Cells(i, 6).Value * jdata.Cells(i, 7).Value), 0) > k = k + 1 > i = i + 1 >Loop この箇所、1セルずつ書き込んでいますから遅いです。 Sheets("A")のデータを配列に取り込んで、同行8列の配列を作って処理し、 Sheets("一覧")に一括で書き込むようにしたほうが速くなります。 具体的には >'i = 2 '今読んでる行 >'k = 2 '書いている行 >'j = 0 '枝番が合った場合 飛ばす行 >'read_no = 0 以上は不要。 : With jdata 'Sheets("A")のA列最下行からG2セルまでのデータ部を配列に v = .Range("G2", .Cells(.Rows.Count, 1).End(xlUp)).Value End With 'vと同サイズ(列は8)の空配列準備 ReDim w(1 To UBound(v), 1 To 8) '書き込み位置 k = 0 '配列1次元の要素の数だけLoop For i = 1 To UBound(v) '一応、Loop終了条件踏襲 If v(i, 1) >= 9500000 Then Exit For End If read_no = v(i, 1) / 10 If v(i, 2) <> 0 Then '枝番有 i = i + v(i, 2) End If k = k + 1 w(k, 1) = Format(read_no, "000000") w(k, 2) = v(i, 3) w(k, 4) = v(i, 4) w(k, 5) = v(i, 5) w(k, 6) = v(i, 6) w(k, 7) = v(i, 7) w(k, 8) = Application.RoundDown((v(i, 4) * v(i, 5) + _ v(i, 6) * v(i, 7)), 0) Next 'kがwの書き込み数なのでSheets("一覧")の範囲をResizeして書き込み jlist.Range("A2:H2").Resize(k).Value = w : こんな感じです。 もしかしたらRoundDown計算は一度Sheets("A")のH列でやってから 配列に一緒に取り込むようにしたほうが速いかもしれません。 また、dictionaryを使った名称セットも 上記Loopの中でやってしまっても良いかもしれませんね。
その他の回答 (3)
- mitarashi
- ベストアンサー率59% (574/965)
3の所だけ試験データを作成してやってみました。xl2000,WindowsXP SP3,PentiumM 1.3Gの古いマシンです。 ご提示のコードより単純な方法で、同等の事をしているつもりですが、数秒で終了しました。ご参考まで。 Sub test3() Dim myDic As Object Dim i As Long Dim targetRange As Range Dim buf As Variant Application.ScreenUpdating = False Set myDic = CreateObject("scripting.dictionary") Set targetRange = Sheets("M").Range("A2:B30001") buf = targetRange For i = LBound(buf, 1) To UBound(buf, 1) myDic.Add buf(i, 1), buf(i, 2) Next i Set targetRange = Sheets("一覧").Range("A2:H35001") buf = targetRange For i = LBound(buf, 1) To UBound(buf, 1) If myDic.exists(buf(i, 2)) Then buf(i, 3) = myDic(buf(i, 2)) Else buf(i, 3) = "無" End If Next i targetRange = buf Application.ScreenUpdating = True End Sub
お礼
ありがとうございます。 皆様にお教え頂いた結果 時間短縮に成功いたしました。
- end-u
- ベストアンサー率79% (496/625)
>..3.のところで2分以上.. 処理3はdictionaryと配列を使ってるのにそんなにかかりますか。 ひょっとしたら別の原因で遅くなってるのかも。 ScreenUpdatingプロパティだけではなく、 (処理前) Dim x As Long With Application x = .Calculation .Calculation = xlCalculationManual .EnableEvents = False .ScreenUpdating = False End With : (処理後) With Application .Calculation = x .EnableEvents = True .ScreenUpdating = True End With のように、イベントと再計算の制御もやったほうが良いかもしれません。 それに処理3を弄るとしたら : Set jname = Worksheets("M") With jname With .Range("B2", .Cells(.Rows.Count, 1).End(xlUp)) v = .Columns(1).Value w = .Columns(2).Value End With End With Set dic = CreateObject("scripting.dictionary") For i = 1 To UBound(v) dic(CStr(v(i, 1))) = i Next With jlist With .Range("B2", .Cells(.Rows.Count, 2).End(xlUp)) v = .Value ReDim z(1 To UBound(v), 0) As String For i = 1 To UBound(v) j = dic(CStr(v(i, 1))) If j = 0 Then z(i, 0) = "無" Else z(i, 0) = w(j, 1) End If Next .Offset(, 1).Value = z End With End With こんな感じでしょうか。
お礼
ありがとうございます。 おかげ様で 3.の処理が10秒程度で終わるようになりました。
- imogasi
- ベストアンサー率27% (4737/17069)
望む回答ではないだろうが、 (1)2つのエクセルシートに、OLEDBを使って他のDBから、 トランザクション「A」のデータを シート「A」に マスタ「M」のデータを シート「M」に展開して、処理はせずに時間を計る(プログラムを加える)。 シートデータを一旦保存。 (2)次に(データベースのデータは使わず・読まず)両シートだけを読んで処理するプログラムに(改変は一部で済むと思うが)変える。 (処理時間を計るプログラムを加える) (2)では現状より相当短縮されるなら、OLEDBを使って他のDBから、エクセルシートへが要因ではないか。 原因追求には、どんなことでも、こういう切り分けが必要だろう。 2重ループもないようだし、コードだけから割り出すのは難しいのでは。 木になるのはFSOのDictionaryという素人受けの仕組みを使っているようなこと。 すばらしい仕組みだが時間はかかるのではないかな。 ソート法でソートし、マッチングアルゴリズムを使えば速くなると思うが、やっている内容がよくわからないので何ともいえない。 === 別のことだが、全体的に何がしたいのか、質問文章で表現できないのか。 質問にはSet jlist = Worksheets("一覧")'処理2からjlist.Range("H1").Value = "金額"までなど1回限りのことで書く必要は無いだろう。 前半は1歩1歩書いてあるようでいて、その後は単純な順処理なのか、マッチング的なことなのか何がしたいのか、判りにくい。 データは再現できないし、テストも出来ない。 もっと読者・回答者のことを慮って、質問の表現・内容を考えててほしい。コードを丸写しでなく、処理内容の解説がほしい。この質問を見たらパスする人が多いのでは。
お礼
ありがとうございます。 文章が判りにくく申し訳ありません。ご親切な皆様に助けていただいています。
お礼
ありがとうございます。 おかげ様で2.の処理があっという間に終わるようになりました。 No.3で頂いた回答と合せて 5分近く掛かっていた処理が30秒掛からず終了するようになりました。 回答して頂いた皆様に感謝いたします。