
A列からE列までの5列で約3000行のデータがあります。これらのデータは2つのグループのデータが混在しています。それをグループ毎に振り分ける処理を作成しました。処理は正常に動作したのですが、処理に数分程度かかりました。
そこで50行毎の処理時間をセルに表示させるようにしたところ、700行程度までは数秒で処理できていますが、その後1分程度、処理がアイドルしています。その後また700~800行を数秒で処理し、またアイドルするの繰り返しでした。
解決策は見つけましたが、処理がアイドルする原因は何として考えられるか、教えていただけませんか。これまでこのような現象は経験がなく、気になっています。よろしくお願いいたします。
問題のステートメント
Cells(idx, 1).Resize(1, 5).Copy Destination:=Cells(cntA, 7).Resize(1, 5)
Application.CutCopyMode = False ' ← この行は、あってもなくても現象は変わらない
なおCopyメソッドをやめて、地道に転記してみたら10秒程度で全ての処理が終わりました。
Cells(cntA, 7) = Cells(idx, 1)
Cells(cntA, 8) = Cells(idx, 2)
Cells(cntA, 9) = Cells(idx, 3)
Cells(cntA, 10) = Cells(idx, 4)
Cells(cntA, 11) = Cells(idx, 5)
Windows10 Office Professional Plus 2019 です。
No.3ベストアンサー
- 回答日時:
こんばんは
内部処理に関係する話なので、以下は勝手な憶測でしかありませんけれど・・
多分、クリップボードのメモリ割り当てなどに関連するのではないかと推測します。
>Application.CutCopyMode = False
は、クリップボードをクリアする処理ではありますが、想像するところ管理部分の履歴をクリアしているだけで、割り当てエリアを完全にクリアしているのではないと想像します。
エリアをオーバーした際にはガベージコレクションのようなことが行われているのではないかと想像します。(そのため若干時間がかかる)
ご質問の内容は環境依存ではありますが、以下で簡単なテストを行ってみました。
速度に関しては、絶対値はPC性能に依存しますので、方法の比較としての値と解釈してください。
まず、5列×30000行の値を単純コピーする方法で、コピーメソッドと、値の転記(Range.Valu=Range.Valueによる)で比べてみました。
・1セルずつでは(後述の理由で)時間がかかるので、1行単位で転記します。
・200行の処理毎に所要時間を計測します。
◇ コピーメソッドを利用した場合
全体で490秒ほどかかりますが、200行ごとの処理時間は2.5~5.5秒とかなりバラツキがあります。
途中で「クリップボードエラー」なるものが発生しました。(添付図)
(私も始めて見ましたが、多分これが原因に関係があるものと想像します。)
上記の表示が出ても、処理が止まることはありません。
200行ごとの処理を時間がかかっている3.5秒以上と、それ以下とに分けて集計してみると
・2.5秒~3.5秒(117回)平均2.95秒
・3.5秒~ (33回)平均4.37秒
となり、明らかに時間がかかる時が33回発生していることがわかります。
(多分、クリップボードのメモリ管理(?)に処理を取られているものと想像)
◇ 値の転記で処理した場合
(ご提示の解決策とほぼ同様の方法です)
1行ずつ30000回の転記ですが、処理時間に多少のバラツキはありますが、クリップボードを介さないこともあり、200行を0.12~0.21秒で処理できます。
全体では、24秒程度で処理が終わりました。
同じ、30000回の処理でクリップボードを介さない方が速いのは、クリップボードの管理以外にも情報量の差が考えられます。
セルのコピーでは、値の他に色や罫線等の書式や関数式、ハイパーリンクや条件付き書式などまでと多くの情報がコピーされますので、「値の転記」が値のみであるのに比べて、遥かに処理するデータ量が多いことが想像できます。
以下は、ご質問の内容からは離れてしまいますが、速度を気になさっているようですので・・
VBAの処理で時間がかかるのは「シートへのアクセス」です。
ですので、1セルずつのアクセスを繰り返すよりも、1行ずつあるいはセル範囲をまとめて処理するほど速度向上が望めます。
また、VBA自体が決して速くはないので、エクセルの種々の機能を利用する方が処理速度は速い場合も多くあります。
以下の3種類の方法を考えて、上記のデータでテストしてみました。
1)エクセルのフィルター機能でグループ別にフィルターをかけ、
全体をコピペ(2回)で処理する。
2)グループ情報を元にソートし、分割してコピペ
3)グループ情報が同じセル範囲を一括でコピペ
方法としては、判定用の作業列を追加し、最後にその列を削除するという方法で行なっています。(サンプルとして、単純に奇数行、偶数行で分割)
1)フィルターの非表示行を省いてペーストするのは、コピーメソッドによらざるを得ませんが、実行時の状態によってかなりバラツキがあるようです。
(多分、クリップボードの使用量が多いのと、非表示行を省く処理のためか?)
全体で0.82秒~5.91秒と実行時の状態(?)により処理時間に差がありました。
2)全データをコピペしてから、判別用の関数式をまとめて作業列に設定。
この結果を元にエクセルの機能でソートし、下半分を別の位置にカット&ペーストという手順で行います。
判別式の設定などは増えますが、データのコピペ(orカット)は2回で済みます。
こちらの場合は、バラツキは少なく、全体をほぼ0.20~0.34秒で処理できます。
(判別式が複雑だと、多少時間が増加する可能性はありますが・・)
3)離れた行でもまとめてコピーすれば詰めてペーストされるという機能(=フィルター状態のコピペと同様)を利用しようと考えたものです。
少ない行数であれば問題なく速くできますが、さすがに30000行が対象だと(実質は15000行程度)セル範囲の計算でハングアップしてしまいました。
改めて対象を10000行にして行ってみると、98秒程かかりました。
どうやら、セル範囲の計算で使用しているUNIONがメッチャ時間がかかるようですね。
データ数が多い場合には、不適のようです。
番外)VBAのメモリを消費するので、上には示しませんでしたが、
v = Range.Value
で、まとめて全データ(=30000行)を読み込んで、グループ別にデータを配列に抜き出して、シートに(まとめて)戻すという方法。
計算は全てメモリ上で行うので、シートへのアクセスは3回で済みます。
事前には速いだろうと予想していましたが、結果は安定の0.34秒でした。
とりあえずの結論としては、0.2~0.3秒の 2)の方法が一番速そうですね。
一番バラツキが少なく安定しているのは、0.34秒の 番外)の方法でした。
以上、回答にはなっていませんけれど、ご参考にでもなれば・・

We?、fujillinさま、私のつまらない疑問にテストまでしてご回答いただき、まことにありがとうございました。
確かにクリップボードのメモリ管理が要因かもしれないですね。今回は「たった3000行の処理に、なぜこんなに時間がかかるのだろう」がきっかけで、調べてみる気になりましたが、これまで気がついていない現象でしたので、勉強になりました。
また高速化の手法についても、複数ご教示いただきありがとうございます。本気で高速化するなら、やはり全部メモリに乗せてしまうのが一番みたいですね。今回は「とりあえず動けば良い」で作ったマクロでしたが、今後の指針とさせていただきます。
No.4
- 回答日時:
No3です。
>「たった3000行の処理に、なぜこんなに時間がかかるのだろう」
あら、3000行でしたかぁ・・
見かけ上停止状態が発生するということだったので、てっきり30000行と読み間違えて、そのまま思い込んでテストしてしまいました。
3000行程度であれば、No3のどの方法でもそれなりの効果はあるものと思います。
No.2
- 回答日時:
描画= Falseにしてるんですね・・・。
Copyメソッドは問題ないと思います。
.Resize(1, 5)が原因だと思います。
実験するしか有りませんが、ループで毎回広げる訳なので、普通に代入する場合の数十倍程度、copyメソッドでやる場合の数百倍程度の時間が掛ると思います。
(さっき試しましたが、大量ループ内でのResizeは驚く程時間掛りました)
vbaからの指令は連続で出ますが、受け取ったエクセル側に時間が掛るので、vba側が待たされる構造ですね。
ssawatakeさま、わざわざテストまでしていただいてありがとうございました。
>大量ループ内でのResizeは驚く程時間掛りました
Resizeはよく使いますが、それで処理時間が延びるとは思ってもいませんでした。勉強になりました。
No.1
- 回答日時:
セルに再描画してるからです。
Timer関数などの問題では無くて、結果をセルに入れてるからです。
エクセル仕様として再描画を止める設定をしない限り、結果を再描画するからです。
再描画に大部分の時間が掛ります。
ループで処理してる訳ですから、再描画が全部完了するまで次の処理が待たされます。
試しに、処理時間だけ計測してセルに結果を入れない場合と入れる場合を比較すれば一目瞭然です。
回答ありがとうございます。質問には書いておりませんでしたが、
Application.ScreenUpdating = False
は、もちろん入れています。
でも、このステートメントがなく毎回再描画したとしても、700行処理して1分アイドルするという挙動になるでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) vbaのエラー対応(実行時エラー7:メモリが不足しています) 4 2023/04/24 00:20
- Visual Basic(VBA) VBA For Each 〜 複数条件について 3 2022/10/20 20:05
- Visual Basic(VBA) 別シートから年齢別の件数をカウントしたい 6 2023/01/23 12:00
- Visual Basic(VBA) VBA listBoxについて 2 2024/03/26 16:14
- Visual Basic(VBA) vbaを早くしたい 5 2022/09/09 10:58
- Visual Basic(VBA) VBAコードが作動しません。修正したいのですが何処に原因かあるか教えて下さい。 1 2024/01/08 16:23
- Visual Basic(VBA) 【前回の続き続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/24 20:49
- Visual Basic(VBA) 別シートから年齢別の件数をカウントしたいの続き 5 2023/01/24 00:16
- Excel(エクセル) 【困っています】VBA 追加処理の記述を教えてください。 1 2022/08/25 22:54
- Visual Basic(VBA) 【困っています2】VBA 追加処理の記述を教えてください。 2 2022/08/26 11:42
このQ&Aを見た人はこんなQ&Aも見ています
-
エクセル:マクロ「Application.CutCopyMode = False」って?
Excel(エクセル)
-
vba クリップボードクリアについて教えてください
その他(プログラミング・Web制作)
-
エクセルVBAが途中で止まります
Visual Basic(VBA)
-
-
4
VBAで保存しないで閉じると空のBookが残る
Excel(エクセル)
-
5
マクロ 新しいシートにデータをコピペしてシートの名前を変更したい
Excel(エクセル)
-
6
VBA シートをコピーする際に Copyメソッドは失敗しましたのエラーが出てしまいます
Visual Basic(VBA)
-
7
VBAでエクセルシートを更新(リフレッシュ)する方法を教えて下さい。
Excel(エクセル)
-
8
エクセルVBAでマルチページの切り替え方法の件で
Excel(エクセル)
-
9
WorkBooksをオープンさせずにシートにコピーしたい【EXCEL VBA】
Excel(エクセル)
-
10
VBAのコマンドボタンの文字列の改行方法は?
Visual Basic(VBA)
-
11
Excel2000/VBA:値と書式のみ貼り付けたい。
Excel(エクセル)
-
12
エクセル マクロ オートフィルの終点の指定について
Excel(エクセル)
-
13
VBA Shapes コピーと名前
Excel(エクセル)
-
14
VBAでセルがコピーされているか判定したい
Excel(エクセル)
-
15
エクセルvbaでの図形のカット(コピー)ペーストについて
Excel(エクセル)
-
16
EXCEL VBA マクロ 実行する度に処理速度がどんどん遅くなる原因が知りたい
Excel(エクセル)
-
17
Excel2013,2016 マクロの動作が停止する?
Visual Basic(VBA)
-
18
VBAの高速化について
Excel(エクセル)
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
「ご処理進めて頂きますようお...
-
エクセルで、日付を入力すると...
-
VBAでループ内で使う変数名を可...
-
男性に対して、『女性への気遣...
-
switch の範囲指定
-
コマンドプロンプトの時間がか...
-
EXCEL VBA マクロ 実行する度に...
-
グーグル地図 ストリートビュ...
-
インタラクティブの反対語は?
-
【Excel】特定の文字を含むセル...
-
ユーザーフォーム「frm_基本❶」...
-
お家デートをしててハグを長い...
-
MacBook AirとMacBook Proの違...
-
BackgroundWorkerの処理
-
フォーム上のすべてのTextBoxを...
-
メルカリのメルカードで買い物...
-
Do~Loopした回数をカウントしたい
-
VBA セルの数値を抽出
-
月度は何て読みますか?
-
RPGプログラムの*HIVALについて
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
「ご処理進めて頂きますようお...
-
エクセルで、日付を入力すると...
-
VBAでループ内で使う変数名を可...
-
【Excel】特定の文字を含むセル...
-
switch の範囲指定
-
EXCEL VBA マクロ 実行する度に...
-
UMLでの例外処理
-
DoEventsがやはり分からない
-
月度は何て読みますか?
-
お家デートをしててハグを長い...
-
VB.NET Excelを読み込んでDataT...
-
VBの質問#if 0 then ってどう...
-
Do~Loopした回数をカウントしたい
-
VBAでCOPYを繰り返すと、処理が...
-
メルカリのメルカードで買い物...
-
インタラクティブの反対語は?
-
Loadイベント中にほかのイベン...
-
リョウ・・・量?料?
-
vba 空のデータをSplitする時の...
-
findは動くがfindnextがマクロ...
おすすめ情報
最終的に、以下のように配列を使って処理を高速化しています。教えていただきたいのはCopyメソッドを使ったときに処理がアイドルしてしまう、考えられる原因(の可能性)です。
Dim Ary
Ary = Cells(idx, 1).Resize(1, 5).value
Cells(cntA, 7).Resize(1, 5).Value = Ary