- ベストアンサー
マクロLOOP文を別方法で高速化
- 質問文章からマクロLOOP文を別の方法で高速化するための要点と対策についてまとめました。
- シート2のボタンをクリックするとSub編集が起動し、他のプロシージャーを呼び出します。現在の処理には約2分かかっており、高速化の方法を探しています。
- Sub編集ではCall文を使用して別のプロシージャーを5つ呼び出していますが、これらの処理は秒速で終わっています。効率の悪い部分を見つけて改善したいです。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
>..Sub test1を改造して >これで動きましたが記述があっているか不安です。 あってますよ。 コメントは >'計算式をA2に入れてコピー。出た値を値貼付 貼付というより '計算式をA2に入れてコピー。計算式を値化。 がしっくりくるかも。 .Range(..).Value = .Range(..).Value 左辺のセル範囲の.Valueプロパティに右辺のセル範囲の.Valueをセットする、 という認識でいいと思います。 ついでに書いておきますと Sub test10() Dim mx As Long Dim i As Long Dim v Dim w() As String '書き出し用 With Sheets("シート●") 'D最終行:F2の値を配列に取る v = .Range("F2", .Cells(.Rows.Count, 4).End(xlUp)).Value mx = UBound(v) '必要サイズの配列を準備。 ReDim w(1 To mx, 1 To 1) 'Loop処理 For i = 1 To mx w(i, 1) = v(i, 1) & v(i, 2) & v(i, 3) Next '書き出し With .Range("■").Resize(mx) .ClearContents .NumberFormat = "@" .Value = w End With End With Erase w End Sub Sub test20() Dim mx As Long Dim i As Long Dim v Dim w() As String '書き出し用 With Sheets("シート●") 'A2:A最終行の値を配列に取る v = .Range("A2", .Cells(.Rows.Count, 1).End(xlUp)).Value mx = UBound(v) '必要サイズの配列を準備。 ReDim w(1 To mx, 1 To 1) 'Loop処理 For i = 1 To mx w(i, 1) = Format$(v(i, 1), "!@@/@@") Next '書き出し With .Range("■").Resize(mx) .ClearContents .NumberFormat = "@" .Value = w End With End With Erase w End Sub 20,000行程度だったら Sub test1改造() と Sub test10() は速度的には大差ないと思います。
その他の回答 (7)
- end-u
- ベストアンサー率79% (496/625)
>計算式を貼り付けて、出た値を値貼付した方が >早いのかな?ですが サンプルとしてはこんな感じです。 Sub test1() With Sheets("シート1") With .Range("R2", .Cells(.Rows.Count, 1).End(xlUp).Offset(, 17)) .NumberFormat = "general" .Formula = "=C2&D2&E2" .NumberFormat = "@" .Value = .Value End With End With End Sub 提示された情報でまとめてみると Sub test2() Dim mx As Long Dim i As Long Dim v Dim w() As String '書き出し用 Dim t As Single t = Timer 'Applicationプロパティを制御。定番です。 With Application .ScreenUpdating = False .EnableEvents = False .Calculation = xlCalculationManual End With With Sheets("シート1") 'A最終行:E2の値を配列に取る v = .Range("E2", .Cells(.Rows.Count, 1).End(xlUp)).Value '添字最大値(セルから配列に取った場合最少値は1) mx = UBound(v) '必要サイズの配列を準備。 ReDim w(1 To mx, 1 To 2) 'Loop処理 For i = 1 To mx w(i, 1) = v(i, 3) & v(i, 4) & v(i, 5) w(i, 2) = Format$(v(i, 1), "!@@/@@") Next '書き出し With .Range("R2:S2").Resize(mx) 'String配列を書き出す時は既データをClearContentsしたほうが速い .ClearContents .NumberFormat = "@" .Value = w End With .Range("B:B,G:G,J:Q").Delete End With With Application .Calculation = xlCalculationAutomatic .EnableEvents = True .ScreenUpdating = True End With Erase w Debug.Print Timer - t End Sub こんな感じになるでしょうか。 >'Applicationプロパティを制御。定番です。 元のコードにこの制御を加えるだけでも改善すると思われます。
お礼
Sub test1() かなり早くなりました。 Sub test2() 1秒以内になりました。 どうもありがとうございます。
補足
大変ありがとうございました。 スレッドの質問にて 「2つのプロシージャーは分けておきたい」 と書いたのは別なものに流用したかったからです。 Sub test2 の中には Sub test1 が組み込まれています。 今回は一番高速なSub test2を使用させていただきます。 今回のやりたかった事は ・R列にC,D,E列を結合した値を転記 ・S列に日付を転記 でした。 で今回の質問とは別なファイルは、 A列にD,E,F列を結合した値を転記だけで 日付の転記は無いです。 教えていただいたSub test1を改造して これで動きましたが記述があっているか不安です。 ↓↓↓↓ Sub test1改造() '2010年11月22日 'A列にD,E,F列の結合した値を転記 '計算式をA2に入れてコピー。出た値を値貼付 With Sheets("シートZ") '式を入れる場所はセルA2。 '最終データがある行の確認を行うのは4=D列。Rows.Countは4。 '式を入れるのはA列なのでD列より左3つ。よってOffsetは-3。 With .Range("A2", .Cells(.Rows.Count, 4).End(xlUp).Offset(, -3)) .NumberFormat = "general" .Formula = "=D2&E2&F2" .NumberFormat = "@" .Value = .Value End With End With End Sub これよりもSub test2の方が高速なので Sub test2から日付転記部分を除去しSub test1の部分だけを 改造しようと挑戦しましたが私の技量では、うまくできませんでした。 Sub test2の改造は私では出来ませんでした。 取り合えず高速化は出来ましたので、どうもありがとうございました。
- merlionXX
- ベストアンサー率48% (1930/4007)
若干早くなった程度ですか・・・。 2000では5千ちょっとしか対応できないTransposeを使っているので20000件は試せませんが、5000件でやったところ Sub 日付02()は 00:00:04 Sub 日付03()は 00:00:00 でした。 Sub 検索キー() 00:00:02 Sub 検索キー02() 00:00:01 です。 そんなに時間がかかるなら別の原因がありそうですね。
お礼
わざわざすいません。 Sub 検索キーのほうですが セルR2 に式を入れて、オートフィルだと 秒速です。 計算式を貼り付けて、出た値を値貼付した方が 早いのかな?ですが その記述を書けません。(ToT)/~~~ これは シート2~6までデータが有って その内容を全てシート1に貼り付けて この2個のプロシージャーを走らせてシート1の値を編集しています。 ファイルサイズは15Mもあります。 とりあえず、シート2~6までは不要なので 削除してシート1だけにしたら 40秒に短縮されました。
- merlionXX
- ベストアンサー率48% (1930/4007)
失礼、Sub 日付02() もFor Nextでまわしてるんでしたね。 では、これも配列に取り込みます。 Sub 日付03() Dim myRng As Range Dim myAr, myBr Dim i As Long With Sheets("シート1") Set myRng = .Range(.Cells(2, "A"), .Cells(Rows.Count, "A").End(xlUp)) myAr = myRng.Value ReDim myBr(LBound(myAr, 1) To UBound(myAr, 1)) For i = LBound(myAr, 1) To UBound(myAr, 1) myBr(i) = Format(myAr(i, 1), "!@@/@@") Next i myRng.Offset(, 18).NumberFormat = "@" myRng.Offset(, 18).Value = Application.Transpose(myBr) End With End Sub
お礼
ありがとうございます。 双方を変更しましたが 若干早くなりました。
- merlionXX
- ベストアンサー率48% (1930/4007)
LOOPは Sub 検索キー ですね。 配列に取り込むならこんな感じかな。 エクセル2003でしたよね? 2000だとエラーになります。 Sub 検索キー02() 'R列にC,D,E列を連結させた値を転記 Dim myV Dim myW Dim x As Long, i As Long With Sheets("シート1") myV = .Range(.Cells(2, "A"), .Cells(2, "A").End(xlDown)).Resize(, 5).Value 'データを配列変数myVに x = UBound(myV, 1) '行数取得 ReDim myW(1 To x) For i = LBound(myV, 1) To UBound(myV, 1) myW(i) = myV(i, 3) & myV(i, 4) & myV(i, 5) '結合データを配列変数myWに Next i .Cells(2, 18).Resize(x, 1).Value = Application.Transpose(myW) '転記 End With End Sub
お礼
ありがとうございます。 数値を文字列としての問題があって 値が9.01+Eとかなる場合がありました。 先頭に(Dim myVの前) Sheets("シート1").Select Columns("R:R").Select Selection.NumberFormatLocal = "@" と入れたら直りました。 速度は少し速くなった感じです。
- Azzz___
- ベストアンサー率40% (9/22)
こんにちは。 既に他の回答の方も書かれていますが、 配列変数を使うと高速できると思います。 イメージとしてはセルに入力されている値をチェックするのではなく、 変数に入れておいた値をチェックする感じですかね。 Do If Cells(行, 1).Value = "" Then Exit Do Cells(行, 18).Value = Cells(行, 3) & Cells(行, 4) & Cells(行, 5) 行 = 行 + 1 Loop 上記のところの前に Dim varData as variant set varData = Worksheet(1).Range("A1").CurrntRegion.Value などとして変数にデータ全部を格納します。 イメージ的には上記変数varDataには表のレイアウトのまんまデータが格納されています。 それをループしてチェックするようにし、 変数に値を入れていき、最後に変数の中身をシートに吐き出す(展開する)形式にすると 処理速度は全然違うと思います。 varDataは配列となっており、ループ処理で添え字(Ubound、Lbound)などで気をつける必要があります。 いかがでしょうか?
お礼
ありがとうございます。 少し早くなりました。
対策1、シートとのアクセス回数を減らす。 対策2、表示はまとめて最後に行う。 Step1、必要なシート情報を配列変数に取り込む。 Step2、表示する配列データを生成する。 Step3、表示する。 シート1のデータが固定性の高いものであれば、構造体変数に取り込みバイナリファイルとして吐き出しておく。この手が可能ならば1秒以内に表示が始まると思います。
お礼
>Step1、必要なシート情報を配列変数に取り込む。 >Step2、表示する配列データを生成する。 >Step3、表示する。 私の技量では対応出来そうもないです。 どうもありがとうございました。
- yuzamasa1101
- ベストアンサー率0% (0/1)
Application.ScreenUpdating = False は入れていますか?
お礼
入れてみましたが、あまり変化はありませんでした。 どうもありがとうございました。
お礼
速くなりました。 本スレッドの質問 ・R列にC,D,E列の値を連結した値を転記 ・S列にA列の日付を編集してS列に転記 にて20,000行の状態で 私の記述では最初は約25秒でした。 Sub test2 のおかげで1秒以内になりました。 ・R列にC,D,E列の値を連結した値を転記 →A列にD,E,F列の値を連結した値を転記 に流用したい >20,000行程度だったら >Sub test1改造( ) と Sub test10( ) は速度的には大差ないと思います。 ↓↓ 最初は私の記述で約2分。 別スレッドで教えていただいた VLOOKUPの高速化(別方法)で6秒になり、 (別方法の部分自体は0.1秒) Sub test1改造( )→ Sub test10( ) に変更で 3秒まで短縮されました。 (Sub test10自体は0.1秒くらい) またコメントの方も教えていただきまして感謝いたします。 本スレッドの質問も高速化され、別ファイルへの流用まで高速化できました。 どうもありがとうございました。
補足
ありがとうございます。 Sub test10( ) は本スレッドの質問とは別の (1)A列にD,E,Fの値を連結して転記 に流用する為に、教えていただいた記述を私が変更した→Sub test1改造 の配列型版?でいいのですよね。 ↓↓ 別ファイルで Sub test1改造 と入れ替えて問題なく動きました。 Sub test20( ) は本スレッドの質問の (2)R列にC,D,E列の値を連結して転記 (3)S列にA列の日付を編集して転記 (4)プロシージャーを分けたい に対して(2)(3)を同時に処理を行う記述の Sub test2( )を (3)のみにした物 (別途 列削除と項目名転記部分は除く) ですよね。 ちなみに Sub test2( ) を(2)と(3)に分ける場合は ・Sub test10( )を記述変更 ・Sub test20( )はそのまま でいいと思い、 Sub test10( )を A列にD,E,Fの値を連結して転記 ↓↓ R列にC,D,Eの値を連結して転記 に修正して Sub test10改造( ) '2010年11月23日 'R列にC,D,E列を連結させた値を転記 '配列型 '最高速型 Dim mx As Long Dim i As Long Dim v Dim w() As String '書き出し用 With Sheets("シート1") '●C最終行:E2の値を配列に取る v = .Range("E2", .Cells(.Rows.Count, 3).End(xlUp)).Value mx = UBound(v) '必要サイズの配列を準備。 ReDim w(1 To mx, 1 To 1) 'Loop処理 For i = 1 To mx w(i, 1) = v(i, 1) & v(i, 2) & v(i, 3) Next '●書き出し With .Range("R2").Resize(mx) .ClearContents .NumberFormat = "@" .Value = w End With End With Erase w End Sub ↓↓ Sub test10改造( ) Sub test20( ) と並べて動作させて ↓↓ Sub test2( ) と同じ結果でした。(記述の変更はこれでいいのですよね。) 本スレッドの質問のファイルは、もうプロシージャーを分ける必要が無くなりましたので 全て連結させたいただきました Sub test2( ) を使用し プロシージャーを分けたかった理由である別ファイルの方は今回教えていただいた Sub test10( ) で行います。 ありがとうございました。