質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

Q&A

5回答

4148閲覧

VBAの効率化

Alice0225

総合スコア206

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

0グッド

1クリップ

投稿2015/11/19 04:44

編集2015/11/19 05:40

VBAの効率化(というより基本的な書き方?)についての質問です。
以前作成したコードを再度流用することとなり、知識0の状態で作っていた酷い有様のコードを
ちょっとはマシな形にしようと修正作業を行っています。
内容としては
ブックAAAの特定の行に○がついていれば同じ行のデータをブックBBBへコピーする
というものです。※○は1~100行の中の1行にしか存在しません。
以前作成したものが↓

VBA

1For B = 1 to 100 2 If Workbooks(AAA).Worksheets("シート1").Cells(B, 2).Value = "○" Then 3 Workbooks(AAA).Worksheets("シート1").Activate 4 Range(Cells(B, 6), Cells(B, 12)).Copy 5 Workbooks(BBB).Worksheets("シート1").Activate 6 Range("AO12:AU12").Select 7 Selection.PasteSpecial Paste:=xlPasteValues 8 End If 9Next B

今回修正を行ったものが↓

VBA

1Dim tempA(50) As Variant 2For B = 1 to 100 3 If Workbooks(AAA).Worksheets("シート1").Cells(B, 2).Value = "○" Then 4 Workbooks(AAA).Worksheets("シート1").Activate 5 For A = 6 To 12 6 temp(A) = Cells(B, A).Value 7 Next A 8 Workbooks(BBB).Worksheets("シート1").Activate 9 For A = 6 To 12 10 Cells(B, A + 35).Value = temp(A) 11 Next A 12 Exit For 13 End If 14Next B

です。
単純にコピー→ペーストだったものを、一度配列に入れてからまとめてセルに代入する形にしました。
(配列もちょっとおかしい使い方ですが、こうでないと頭の中でこんがらがってしまうのです…)

このような処理を行う場合の方法としては修正後のような方法であっているのでしょうか。
日々勉強しながらコードを書いている為、サイトによって方法がバラバラで自身ではよくわかっていない状況です。。。

【改めて今回の質問内容】
①今回の修正後コードの効率は100点満点で採点すると何点ほどになりますか?(回答者様の主観で結構です)
②修正後コードを改善(またはまったく別の方法へ変更)するとした場合、どのようなやり方が考えられますか?
③「VBAの勉強をするならこのサイト!」のようなものがありましたらお教え下さい。

以上、長文乱文申し訳ございませんが皆様のお力をお貸し頂ければ幸いでございます。

追記
【使用環境】
Windows 7 Pro 32bit
Microsoft Excel 2010

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

Copy, Paste でクリップボード経由より、直接代入したほうが高速だと思います。

配列を使うなら、バリアント配列を使うと効率的かな。

セル範囲は、バリアント配列に一括して代入できますし、
また、バリアント配列を一括してセル範囲に代入できますので。
(セル範囲をバリアント配列に代入した時は、2次元配列になります。)

vba

1 Dim tempA As Variant, tempB As Variant, i As Long 2 3 tempA = Workbooks(AAA).Worksheets("シート1").Range("B1:B10") 4 For i = 1 To 10 5 If tempA(i, 1) = "○" Then 6 tempB = Workbooks(BBB).Worksheets("シート1").Range("F" & i & ":L" & i) 7 Workbooks(AAA).Worksheets("シート1").Range("AO12:AU12") = tempB 8 Exit For 9 End If 10 Next

ワークシート関数を使えば、もっとシンプルに記述できます。

VBA

1 Dim i As Long 2 3 i = WorksheetFunction.Match("○", _ 4 Workbooks(AAA).Worksheets("シート1").Range("B1:B10"), 0) 5 Workbooks(BBB).Worksheets("シート1").Range("AO13:AU13").Value = _ 6 Workbooks(AAA).Worksheets("シート1").Range("F" & i & ":L" & i).Value

どちらが高速なのかは、分かりませんが、下記によるとワークシート関数が一番高速のようです。

Office TANAKA - Excel VBA高速化テクニック[セルを配列に入れる]

投稿2015/11/19 07:22

編集2015/11/19 07:50
hatena19

総合スコア33699

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Alice0225

2015/11/24 02:05

ワークシート関数は初耳な単語ですね…調べたいと思います。 バリアント配列に範囲一括代入が出来るのも知りませんでした。 とても勉強になります!
guest

0

①あくまでコードの評価という視点で、10点(前)→20点(後)
また、少しばかりのアドバイスですが、以下の点を述べておきます。
・変数の宣言を強制した方が良いです(Option Explicit)
・RangeやCells,Worksheetsには、必ず[.]を付けて親(所属)を明確にする
・'Avtivateに頼らない
・For文で1to100としていますが、100は動的に取る方法を考えた方が良い


・修正前の状態で、修正をするとしたら

With Workbooks(AAA).Worksheets("シート1") For B = 1 To 100 If .Cells(B, 2).Value = "○" Then .Range(.Cells(B, 6), .Cells(B, 12)).Copy With Workbooks(BBB).Worksheets("シート1") .Range("AO" & B).PasteSpecial Paste:=xlPasteValues End With Application.CutCopyMode = False 'コピー状態を終了 Exit For End If Next B End With

・修正後の状態で、修正するとしたら

Private Sub Sample1() Dim ShA As Worksheet Dim ShB As Worksheet Dim ValAry As Variant Dim TempAry As Variant Dim i As Long Dim Col_T As Long Dim Col_S As Long Dim Col_E As Long Set ShA = Workbooks("AAA").Worksheets("シート1") Set ShB = Workbooks("BBB").Worksheets("シート1") With ShA Col_T = 2 ValAry = .Range(.Cells(1, Col_T), .Cells(100, Col_T)).Value End With For i = LBound(ValAry, 1) To UBound(ValAry, 1) If ValAry(i, 1) = "○" Then Col_S = 6 Col_E = 12 With ShA TempAry = .Range(.Cells(i, Col_S), .Cells(i, Col_E)).Value End With ShB.Cells(i, Col_S).Offset(, 35).Resize(1, UBound(TempAry, 2) - LBound(TempAry, 2) + 1).Value = TempAry Exit For End If Next Set ShA = Nothing Set ShB = Nothing End Sub

・自分でコーディングするとしたら

Private Sub Sample1() Dim ShA As Worksheet Dim ShB As Worksheet Dim RngA As Range Dim RngB As Range Dim ValAry As Variant Dim i As Long Dim Row_S As Long Dim Row_E As Long Dim Row_T As Long Dim Col_T As Long Dim Col_S As Long Dim Col_E As Long Dim Col_Off As Long Set ShA = Workbooks("AAA").Worksheets("シート1") Set ShB = Workbooks("BBB").Worksheets("シート1") ' Set ShA = Sheet1 ' Set ShB = Sheet2 'Findは回数を重ねると遅いので、配列を使用する方法を使います。(1万行程度まで大丈夫…なはずです) With ShA Col_T = 2 Row_S = 1 Row_E = 100 '場合によっては動的に取得 ValAry = .Range(.Cells(Row_S, Col_T), .Cells(Row_E, Col_T)).Value End With For i = LBound(ValAry, 1) To UBound(ValAry, 1) If ValAry(i, 1) = "○" Then Col_S = 6 Col_E = 12 Col_Off = 35 '配列の要素と、行数が異なる場合に対応して行数調整 Row_T = Row_S + i - 1 With ShA Set RngA = .Range(.Cells(Row_T, Col_S), .Cells(Row_T, Col_E)) End With With ShB Set RngB = .Range(.Cells(Row_T, Col_S), .Cells(Row_T, Col_E)).Offset(, Col_Off) End With Call RngA.Copy(RngB) '値だけで良ければ: RngB.Value = RngA.Value Exit For End If Next Set RngA = Nothing Set RngB = Nothing Set ShA = Nothing Set ShB = Nothing End Sub


自分の場合は、周囲に教えて頂ける方々がいたのですが、
何かに困って調べた時には、よく田中さんのサイトにお世話になったと思います。
http://officetanaka.net/excel/index.htm
困った時に検索して、Hitしていたら優先的に見る、というような感じでした。


VBAは軽視されがちですが、奥が深いです。
使いこなせれば良いパートナーとなってくれるはずですので、
頑張ってください。

投稿2015/11/23 09:17

ExcelVBAer

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Alice0225

2015/11/24 01:59

ご回答ありがとうございます。 ご指摘頂きました ①変数の宣言を強制した方が良いです(Option Explicit) ②RangeやCells,Worksheetsには、必ず[.]を付けて親(所属)を明確にする ③'Avtivateに頼らない ④For文で1to100としていますが、100は動的に取る方法を考えた方が良い につきましては ①宣言部分に関しては長くなってしまうので省略してコードを記載させて頂きました。 ②アドバイス通り修正いたします。 ③アドバイス通り修正いたします。 ④For文に関しては①と同様長くなるのでコードを省略しております。  実際にはMaxRowを使用したつくりになっています。 その他ご提示頂いたコードに関しましてもありがとうございました。 知識不足で理解に時間がかかりそうですが勉強しつつ実装・修正にあたりたいと思います!
guest

0

マクロを流すことでクリップボードの中身を書き換えられてしまうのはあまり好まれませんよね。
その点を改善しようとしたこと自体が評価に値すると思います。
コーディングについては、変数名がわかりにくいところが減点です。
ちょっとしたプログラムのちょっとした変数でも、ポリシーを持って記述したほうが後後のためになりますよ。

改善案・・・というか参考情報のようなものですが、今回のように値のみのコピーでよくて、コピー元とコピー先が同じ並び順でよいのなら、rangeからrangeに値を写す方法が使えます。
また、対象行の検索方法についても選択肢のひとつとしてFindを使う方法を紹介してみます。

処理速度や使い勝手で使いやすいものを選んでみてください。

VBA

1 Dim s1 As Worksheet 'コピー元シート 2 Dim s2 As Worksheet 'コピー先シート 3 4 Set s1 = Sheets("Sheet1") 5 Set s2 = Sheets("Sheet2") 6 7 Dim rngSearch As Range '検索範囲 8 Dim rngFind As Range '検索結果 9 Dim strFirstAdr As String '最初に見つかった検索結果のアドレス 10 11 Dim rngCopyFrom As Range 'コピー元 12 Dim rngCopyTo As Range 'コピー先 13 Dim intCopyCnt As Integer 'コピー件数 14 15 '検索範囲の設定 16 'Set rngSearch = s1.Range("B:B") 17 Set rngSearch = s1.Range("B1:B100") 18 '○を検索 19 Set rngFind = rngSearch.Find("○") 20 21 If Not (rngFind Is Nothing) Then 22 23 strFirstAdr = rngFind.Address 24 25 Do 26 'コピー件数 27 intCopyCnt = intCopyCnt + 1 28 'コピー元の範囲設定 29 Set rngCopyFrom = s1.Range("F" & CStr(rngFind.Row) & ":L" & CStr(rngFind.Row)) 30 'コピー先の範囲設定 31 Set rngCopyTo = s2.Range("AO" & CStr(rngFind.Row) & ":AU" & CStr(rngFind.Row)) 32 33 '値のコピー 34 rngCopyTo.Value = rngCopyFrom.Value 35 36 '次の対象を検索 37 Set rngFind = rngSearch.FindNext(rngFind) 38 39 If rngFind Is Nothing Then Exit Do 40 41 Loop Until rngFind.Address = strFirstAdr 42 43 End If

投稿2015/11/20 02:22

jawa

総合スコア3013

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Alice0225

2015/11/24 02:03

コードのご提示ありがとうございます。 質問欄に書いたものは全体の一部でして、 A1:A5をコピーして別ブックへ貼り付ける だけでよい場合と A1,A4,A7,A11,A15をコピーして別ブックへ貼り付ける 様な場合があったりします。 Findやアドレスを使った方法は知りませんでしたので、頂きましたアドバイスを元に今回のコードに適用できるか試してみたいと思います^^
guest

0

VBAマクロなんてもう何年もいじっていないのですっかり忘れていてなんだったか思い出せなかったのですが、ようやく思い出せました。
たくさんのセルを書き換えたりするような場合は画面の更新を停止すれば速くなります。
最初にApplication.ScreenUpdating = Falseとやって画面の更新を停止し、
最後にApplication.ScreenUpdating = Trueとやれば更新を再開します。

投稿2015/11/19 07:00

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Alice0225

2015/11/19 07:02

ご回答ありがとうございます。 そのあたりの処理は記載してありますので問題ありません。 今回は内部の重要部分コードのみの修正になります。
catsforepaw

2015/11/19 07:06

なるほど、失礼しました。
Alice0225

2015/11/24 02:06

こちらこそ省略省略で書いてしまっていて申し訳ございませんでした…。
guest

0

印象としてはむしろ改悪されているように思います。
必要ない配列を宣言し(しかもバリアント型)、ループを無駄に多くとっています。
根本的な問題として、質問者様は**「アクティブ状態のシートでなければコピー・貼り付けができない」と思い込んでいませんか?**
セルの操作(セルのコピー)

For B = 1 to 100 If Workbooks(AAA).Worksheets("シート1").Cells(B, 2).Value = "○" Then Workbooks(AAA).Worksheets("シート1").Range(Cells(B, 6), Cells(B, 12)).Copy Workbooks(BBB).Worksheets("シート1").Range("AO12:AU12").PasteSpecial Paste:=xlPasteValues End If Next B

一応改善しようとするとこうなりますが、これだとコピー先に上書きされ続けて最後に貼り付けたものしか残らないのでは…?

投稿2015/11/19 05:11

編集2015/11/19 05:26
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Alice0225

2015/11/19 05:46 編集

実行に掛かる時間に関してだけ言えば約6分の1に短縮されていたので、改善されていると思っておりました。 試しに Workbooks(AName).Worksheets("Sheet1").Range("A1:A6").Copy Workbooks(BName).Worksheets("Sheet1").Range("B1:B6") を試してみましたが上手く範囲コピーされないのはなぜでしょう? 追記 ちなみに○は1~100行目のどこか1行にしかありませんすみません。 範囲指定が完全に間違っていました… 範囲コピーに関しては問題なさそうです。
swordone

2015/11/19 05:42

改行されているからではないでしょうか? ひとつづきの命令を複数行に跨がって書くには行継続文字が必要です。
hatena19

2015/11/19 07:30

Workbooks(AAA).Worksheets("シート1").Range(Cells(B, 6), Cells(B, 12)) だと、Cells はアクティブなシートを参照するのでは? Activateしないなら、 With Workbooks(AAA).Worksheets("シート1") .Range(.Cells(B, 6), .Cells(B, 12)) End With かな?
swordone

2015/11/19 07:57

そうでした、そのことを失念していました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問