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

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

新規登録して質問してみよう
ただいま回答率
85.35%
ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

VBA

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

マクロ

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

Q&A

解決済

2回答

4285閲覧

ExcelファイルからExcelファイルへの転記マクロ作成(条件付き)

momo2134

総合スコア6

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

VBA

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

マクロ

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

0グッド

0クリップ

投稿2020/04/01 10:28

編集2020/04/02 01:24

前提・実現したいこと

ExcelファイルからExcelファイルへの転記マクロ作成(条件付き)

以下目的を達成する方法をご教示いただきたいです
エンジニアではないため、プログラミングへの理解が至らない部分もありますが、宜しくお願い致します

【前提】
顧客からの問い合わせをExcelファイルで管理しています(問い合わせ1件につき、ファイル1つ)
問い合わせファイル内には、問い合わせ内容・日時、対応内容・日時などを記載しており、
ファイルは問い合わせへの対応のステータス毎(「未対応」、「対応中」、「対応済み」)
にフォルダに分けています(手作業)

【現状】
これら全てのファイルを一覧にするマクロを作成しました(問い合わせ1件を1レコードとして一覧に転記)

【目的】
問い合わせファイルが増え、マクロの実行に時間がかかるようになったため、
一覧ファイルの特定のカラム(対応日時)が埋まっている場合は転記せず、
空白の場合のみ、問い合わせファイルから転記するようプログラムを修正したいです

【問題】
条件を追加したところ、条件を反映されておらず、【
現状】同様、問い合わせファイル全件が一覧ファイルに転記されてしまいます

【前提条件】
・一覧ファイルは、問い合わせファイルを格納するステータス毎のフォルダと同フォルダです
・一覧ファイルへの転記は、12行目から行います

発生している問題・エラーメッセージ

エラーメッセージはなく、マクロ自体は正常に終了します

該当のソースコード

VBA

1'※コメントアウトしている箇所は一部伏せさせていただいております 2 3 '変数宣言 4 Dim strDefPath As String 5 Dim strActiveSheet As String 6 Dim strMyBook As String 7 Dim fileName As String 8 Dim ctrlNum, ny_ctrlNum As String 9 10 '定数宣言 11 Const strInSheet As String = "お客様問い合わせファイル" 12 Const strDefFlID As String = "問い合わせ" 13 Const strTAIOUZUMI = "\30 対応済み" 14 Const strTAIOUCHU = "\20 対応中" 15 Const strMITAIOU = "\10 未対応" 16 17Public Sub 一覧作成() 18 19 Dim fs As FileSearch 20 Dim fso As Object 21 Dim file, folder As Variant 22 Dim intRet, i, row As Integer 23 Dim lngi As Long 24 Dim lngOutRow As Long 25 Dim cell As Variant 26 27 '一覧作成の実行確認 28 intRet = MsgBox("表を最新の状態にします。よろしいですか?", vbOKCancel, "確認") 29 If intRet <> vbOK Then 30 Exit Sub 31 End If 32 33 '自動更新しないように設定 34 With Application 35 .Calculation = xlCalculationAutomatic 36 .MaxChange = 0.001 37 End With 38 ActiveWorkbook.PrecisionAsDisplayed = False 39 40 'シートの非表示行を表示する 41 Cells.Select 42 Selection.EntireColumn.Hidden = False 43 44 '画面の更新を止める 45 Application.ScreenUpdating = False 46 47  '★今回の条件追加のために、取得 48 '対応完了日(AQ列)が空白の行の管理NOを取得する 49 '対応完了日(AQ列)が空白でない行の管理NOを取得する 50 51 For Each cell In Range("AQ12:AQ3000") 52 53If cell = "" Then 54 ny_ctrlNum = cell.Offset(0, -39) 55 ElseIf cell <> "" Then 56 ctrlNum = cell.Offset(0, -39) 57 End If 58 Next 59 60  '★今回の条件追加のために以下のように変更 61    '変更前:12行目以降すべてクリア 62    '変更後:対応完了日が空白の行のみクリア 63 '対応完了日(AQ列)が空白の行の値をクリアする 64 For Each cell In Range("AQ12:AQ3000") 65 If cell = "" Then 66 row = Range("AQ12").row 67 Rows(row).ClearContents 68 End If 69 Next 70 71 'ファイルのパスを取得する' 72 strMyBook = ActiveWorkbook.Name 73 strActiveSheet = ActiveSheet.Name 74 Set fso = CreateObject("Scripting.FileSystemObject") 75 76 lngOutRow = 11 77 78 '対応済みフォルダ内の検索 79 strDefPath = ActiveWorkbook.Path & strTAIOUZUMI 80 For Each file In fso.GetFolder(strDefPath).Files 81 lngOutRow = lngOutRow + 1 82 intRet = SHEET_PROC(file.Path, strMyBook, lngOutRow) 83 If intRet = 1 Then 84 lngOutRow = lngOutRow - 1 85 ElseIf intRet = 9 Then 86 MsgBox "エラーのため強制終了します。" 87 End If 88 Next 89 90 '対応中フォルダ内の検索 91 strDefPath = ActiveWorkbook.Path & strTAIOUCHU 92 For Each file In fso.GetFolder(strDefPath).Files 93 lngOutRow = lngOutRow + 1 94 intRet = SHEET_PROC(file.Path, strMyBook, lngOutRow) 95 If intRet = 1 Then 96 lngOutRow = lngOutRow - 1 97 ElseIf intRet = 9 Then 98 MsgBox "エラーのため強制終了します。" 99 End If 100 Next 101 102 '未対応フォルダ内の検索 103 strDefPath = ActiveWorkbook.Path & strMITAIOU 104 For Each file In fso.GetFolder(strDefPath).Files 105 lngOutRow = lngOutRow + 1 106 intRet = SHEET_PROC(file.Path, strMyBook, lngOutRow) 107 If intRet = 1 Then 108 lngOutRow = lngOutRow - 1 109 ElseIf intRet = 9 Then 110 MsgBox "エラーのため強制終了します。" 111 End If 112 Next 113 114 Call set_num 115 116 Range("A3").Select 117 118 MsgBox "一覧作成しました。" 119 120 '画面描画を再開 121 Application.ScreenUpdating = True 122 123 Exit Sub 124 125End Sub 126 127 128Function SHEET_PROC(strInFl As String, strOutBook As String, lngRowOut As Long) As Integer 129 130 Dim strWk As String 131 Dim strInBook As String 132 Dim intPos As Integer 133 Dim wbIn As Workbook 134 135 SHEET_PROC = 9 136 137 'リンクを更新せずにファイルを開く 138 Set wbIn = Workbooks.Open(strInFl, 0) 139 140 intPos = InStr(strInFl, strDefFlID) 141 142 strInBook = Mid(strInFl, intPos) 143 144  '★今回の条件追加のために、IF文を追加 145 If strInBook <> ctrlNum Then 146 147 ' 148 ' 149 Workbooks(strOutBook).Worksheets(strActiveSheet).Cells(lngRowOut, 2).Value = wbIn.Worksheets(strInSheet).Cells(10, 33).Value 150 Workbooks(strOutBook).Worksheets(strActiveSheet).Cells(lngRowOut, 3).Value = wbIn.Worksheets(strInSheet).Cells(10, 34).Value 151 ' 152 '※質問の文字数の都合上略します 153 '(略) 154 Workbooks(strOutBook).Worksheets(strActiveSheet).Cells(lngRowOut, 69).Value = wbIn.Worksheets(strInSheet).Cells(2, 46).Value 155 156 'ファイルを保存せずに閉じる 157 wbIn.Close SaveChanges:=False 158 SHEET_PROC = 0 159 End If 160 161Exit Function 162 163End Function 164 165 166Sub set_num() 167 168 Dim WBook As String 169 Dim ASheet As String 170 Dim L As Integer 171 Dim n As Integer 172 173 'ソート 174 Rows("11:3000").Select 175 Selection.Sort Key1:=Range("D12"), Order1:=xlAscending, _ 176 Key2:=Range("E12"), Order2:=xlAscending, Header:=xlYes, _ 177 OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, SortMethod:=xlPinYin 178 179 'Rif番号付け 180 L = 12 181 n = 1 182 183 WBook = ActiveWorkbook.Name 184 ASheet = ActiveSheet.Name 185 186 While Workbooks(WBook).Worksheets(ASheet).Cells(L, 4).Value <> "" 187 Workbooks(WBook).Worksheets(ASheet).Cells(L, 1).Value = n 188 L = L + 1 189 n = n + 1 190 Wend 191 192End Sub 193
### 試したこと Gotoステートメントの利用など試したのですが、いずれも想定通りの結果にはなりませんでした。 ### 補足情報(FW/ツールのバージョンなど) Excel2016

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

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

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

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

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

end-u

2020/04/01 14:12

'シートの非表示行を表示する Cells.Select Selection.EntireColumn.Hidden = False これはCells.EntireColumn.Hidden = FalseというようにSelectしないでも書けるし Columns.Hidden = Falseと書ける 何より、 Cells.Select..とか Range("AQ12:AQ3000")..とか、Rangeの親が何なのか指定していないので、 コード実行時にActiveだったSheetの処理になっていますから危ういです Dim wb As Workbook Set wb = ActiveWorkbook 'とかコードがある ThisWorkbookとか Dim ws As Worksheet Set ws = wb.Sheets("xxx") などオブジェクト変数をうまく使って ws.Cells... ws.Range("AQ12:AQ3000")... Active状況依存しないコードにしたほうが良いです
end-u

2020/04/01 14:15

まず確認箇所がQ1~Q3まで '対応完了日(AQ列)が空白の行の管理NOを取得する '対応完了日(AQ列)が空白でない行の管理NOを取得する For Each cell In Range("AQ12:AQ3000")   If cell = "" Then     ny_ctrlNum = cell.Offset(0, -39)   ElseIf cell <> "" Then     ctrlNum = cell.Offset(0, -39)   End If Next Q1) Range("AQ12:AQ3000")は必ず固定範囲ですか? それとも入力済み範囲に限定したいけど変動範囲の指定の仕方がわからなかったのでしょうか? ex)ws.Range("AQ12", ws.Cells(ws.Rows.Count, "AQ").End(xlUp)) Q2) "AQ12:AQ3000"までLoopしてますが 変数 ny_ctrlNum と ctrlNum がLoopの度に上書きされますから、 最後の1つずつしか取得しませんが、そのような仕様で良いですか? '対応完了日(AQ列)が空白の行の値をクリアする For Each cell In Range("AQ12:AQ3000")   If cell = "" Then     row = Range("AQ12").row     Rows(row).ClearContents   End If Next Q3) これも"AQ12:AQ3000"までLoopしてますが cell = "" の条件を満たしたとしても、 row = Range("AQ12").row '★ Rows(row).ClearContents AQ12セル固定してますから12行目が毎回ClearContentsされてしまいます ひょっとして、2行まとめて cell.EntireRow.ClearContents で良かったりしますか? それに上のLoopと同じ範囲なので、まとめて処理できそうですよね
end-u

2020/04/01 14:44

'ファイルのパスを取得する' strMyBook = ActiveWorkbook.Name strActiveSheet = ActiveSheet.Name この箇所などは、Book名、Sheet名を文字列で取得して Workbooks(strOutBook).Worksheets(strActiveSheet)など、その文字列で再指定してますが さっき指摘したように、 Dim ws As Worksheet Set ws = ActiveSheet とWorksheetオブジェクト変数に入れてしまい、 Function SHEET_PROC(strInFl As String, strOutBook As String, lngRowOut As Long) As Integer これも intRet = SHEET_PROC(file.Path, ws, lngOutRow) Function SHEET_PROC(strInFl As String, outSht As Worksheet, lngRowOut As Long) As Integer などとして、Worksheet型でそのまま渡せば outSht.Cells(lngRowOut, 2).Value = wbIn.Worksheets(strInSheet).Cells(10, 33).Value outSht.Cells(lngRowOut, 3).Value = wbIn.Worksheets(strInSheet).Cells(10, 34).Value とできます
Y.H.

2020/04/02 00:24

> 条件を追加したところ、条件を反映されておらず 質問記載のソースコードのどこが「条件を追加した」部分なのかわかるように質問を編集し追記ください。
momo2134

2020/04/02 01:14 編集

@end_uさん ご指摘いただきありがとうございます。 1番目と3番目のご指摘に関しては、ソースに反映しますので、 可能であれば最終的にソースをご確認いただきたいと思います。 2番目のご指摘・ご質問に関して、以下ご返信致します。 > まず確認箇所がQ1~Q3まで > '対応完了日(AQ列)が空白の行の管理NOを取得する > '対応完了日(AQ列)が空白でない行の管理NOを取得する > For Each cell In Range("AQ12:AQ3000") >   If cell = "" Then >     ny_ctrlNum = cell.Offset(0, -39) >   ElseIf cell <> "" Then >     ctrlNum = cell.Offset(0, -39) >   End If > Next Q1) > Range("AQ12:AQ3000")は必ず固定範囲ですか? > それとも入力済み範囲に限定したいけど変動範囲の指定の仕方がわからなかったのでしょうか? > ex)ws.Range("AQ12", ws.Cells(ws.Rows.Count, "AQ").End(xlUp)) →ご指摘の通り、入力済み範囲に限定したかったのですが、指定の仕方がわからず、一旦3000行までとしていました。最終的にソースにご指摘を反映致します。 Q2) > "AQ12:AQ3000"までLoopしてますが > 変数 ny_ctrlNum と ctrlNum がLoopの度に上書きされますから、 > 最後の1つずつしか取得しませんが、そのような仕様で良いですか? →条件を満たす場合、ny_ctrlNum と ctrlNum は毎回取得したいです。 あわせてQ3に修正案を記載します。ご確認をお願いします。 Q3) > '対応完了日(AQ列)が空白の行の値をクリアする > For Each cell In Range("AQ12:AQ3000") >   If cell = "" Then >     row = Range("AQ12").row >     Rows(row).ClearContents >   End If > Next > これも"AQ12:AQ3000"までLoopしてますが > cell = "" の条件を満たしたとしても、 > row = Range("AQ12").row '★ > Rows(row).ClearContents > AQ12セル固定してますから12行目が毎回ClearContentsされてしまいます > ひょっとして、2行まとめて > cell.EntireRow.ClearContents > で良かったりしますか? > それに上のLoopと同じ範囲なので、まとめて処理できそうですよね →以下修正案です。まだ実行していないため、不備がありましたら申し訳ございません。 Dim wb As Workbook Dim i As Long Set wb = ActiveWorkbook '対策完了日(AQ列)が空白の行の管理NOを取得する '対策完了日(AQ列)が空白でない行の管理NOを取得する '対策完了日(AQ列)が空白の行の値をクリアする For i = Cells(Rows.Count, 41).End(xlUp).Row To 12 Step -1  If Cells(i, 41).Value = "" Then    ny_ctrlNum = cell.Offset(i, -39)    cells(i, 41).EntireRow.ClearContents  ElseIf Cells(i, 41).Value <> "" Then    ctrlNum = cell.Offset(i, -39)  End If Next
momo2134

2020/04/02 01:29

@Y.H.さん ご指摘をいただき、今回条件を追加した箇所に"★"で内容を記載しました。 ソース内でわかりづらいですが、ご了承ください。 (上の方にいただいたご指摘などはまだ反映しておらず、質問時のソースのままとしています) 宜しくお願い致します。
end-u

2020/04/02 02:09

> →条件を満たす場合、ny_ctrlNum と ctrlNum は毎回取得したいです。 ..という事であれば、 > For i = Cells(Rows.Count, 41).End(xlUp).Row To 12 Step -1 >  If Cells(i, 41).Value = "" Then >    ny_ctrlNum = cell.Offset(i, -39) >    cells(i, 41).EntireRow.ClearContents     ☆ここで対策完了日(AQ列)が空白の行の管理NOを取得 してその後の処理をする     ...いわゆる『更新』でしょうか     上書きするなら.EntireRow.ClearContentsは必要ないのでは。 >  ElseIf Cells(i, 41).Value <> "" Then >    ctrlNum = cell.Offset(i, -39)     ☆対策完了日(AQ列)が空白でない行の管理NOを取得 してその後の処理をする >  End If > Next ..という処理にするか、 ny_ctrlNum と ctrlNum を配列変数にして、 セル範囲Loop後、その変数をLoopしてまとめて処理するか、になりそうです ですがそもそも > 問い合わせファイルが増え、マクロの実行に時間がかかるようになったため、 という点が仕様改訂の理由なら、現状コードをブラッシュアップさせて速度改善を図った方が良い気がします? 最大3,000ファイルでしょうか? 実のところ3フォルダ合わせてどれくらいの量で、どれくらいの時間がかかっているのでしょう SHEET_PROCは1セルずつ書き込みにいってる為、それは時間かかっちゃいますね 配列に取り込んで書き出しは1回で済ますとか ファイル閉じたまま数式で参照して値化させるとか 改善の方法はありそうです
momo2134

2020/04/02 03:16 編集

@end-uさん >> →条件を満たす場合、ny_ctrlNum と ctrlNum は毎回取得したいです。 >..という事であれば、 →ソースを再度見直した結果、ny_ctrlNumは取得不要でした。 試行錯誤していた際の不要な変数です。申し訳ございません。 >> For i = Cells(Rows.Count, 41).End(xlUp).Row To 12 Step -1 >>  If Cells(i, 41).Value = "" Then >>    ny_ctrlNum = cell.Offset(i, -39) >>    cells(i, 41).EntireRow.ClearContents >    ☆ここで対策完了日(AQ列)が空白の行の管理NOを取得 してその後の処理をする >    ...いわゆる『更新』でしょうか >    上書きするなら.EntireRow.ClearContentsは必要ないのでは。 >>  ElseIf Cells(i, 41).Value <> "" Then >>    ctrlNum = cell.Offset(i, -39) >    ☆対策完了日(AQ列)が空白でない行の管理NOを取得 してその後の処理をする >>  End If >> Next →処理として、「対応完了日(AQ列)が空白の行(旧レコード)の管理NOを取得し、 その管理NOと一致する問い合わせファイルのみを一覧に追加(新レコード)する」ことをイメージしていたため、 旧レコードのクリアが必要という認識でした。(上書きする仕方がわからなかったという理由もありますが、、、) > ..という処理にするか、 > ny_ctrlNum と ctrlNum を配列変数にして、 →ctrlNumを配列変数にする場合、データ数は「ファイル数」という考え方でよろしいでしょうか? > セル範囲Loop後、その変数をLoopしてまとめて処理するか、になりそうです > ですがそもそも >> 問い合わせファイルが増え、マクロの実行に時間がかかるようになったため、 > という点が仕様改訂の理由なら、現状コードをブラッシュアップさせて速度改善を図った方が良い気がします? > 最大3,000ファイルでしょうか? > 実のところ3フォルダ合わせてどれくらいの量で、どれくらいの時間がかかっているのでしょう →現状ファイル数は600ほどで、処理に1時間~1.5時間かかっています。  これほど時間がかかるのは、他の要因もあるかもしれません。。。 > SHEET_PROCは1セルずつ書き込みにいってる為、それは時間かかっちゃいますね > 配列に取り込んで書き出しは1回で済ますとか > ファイル閉じたまま数式で参照して値化させるとか > 改善の方法はありそうです →ネットにも配列にした方が早いという情報があり、検討したのですが、 問い合わせファイル内は、セルが結合されていたりと転記元のセルに規則性がなく、実現方法がわからず、 転記するファイルを絞ることで処理時間を早くしようと考えました。 可能であれば、処理時間は早ければ早い方がよく、 配列に取り込む + 書き込むファイル数も絞る ができればよいと考えているのですが、 可能なものでしょうか? 何度もご返信いただきありがとうございます。
guest

回答2

0

ベストアンサー

[A] [B] [C] [D] [1] 問い合わせ一覧表 [2] [3] 件名 対応日時 対応状況 内容 [4] [5]

こんな表がマクロブックの一番左のシートに作ってあるとして、
以下のようなことがしたいのでは?

ExcelVBA

1Option Explicit 2 3Dim mFSO As FileSystemObject 4 5Sub 一覧表更新() 6 Dim rngList As Range 7 Dim vrtSubjectList As Variant 8 9 'FileSystemObjectを呼び出す(実体化) 10 Set mFSO = New FileSystemObject 11 12 '一覧表のセル範囲取得 13 Set rngList = ThisWorkbook.Worksheets(1).Range("A3").CurrentRegion 14 15 '空欄クリア 16 InitializeTable rngList 17 18 '一覧にないファイルのリストを取得 19 vrtSubjectList = Get_UpdatedSubjectList(rngList) 20 21 'データの転記 22 SetUpdated vrtSubjectList, rngList 23End Sub 24 25'表中の対応日時が空欄の行をクリア 26Private Sub InitializeTable(ByRef rngList As Range) 27 Dim rngBlank As Range 28 29 With rngList 30 If rngList.Rows.Count > 1 Then 31 On Error Resume Next 32 Set rngBlank = rngList.Columns(2).SpecialCells(xlCellTypeBlanks) 33 On Error GoTo 0 34 If rngBlank Is Nothing Then Exit Sub 35 36 rngBlank.EntireRow.ClearContents 37 rngList.Sort Key1:=rngList(2), Order1:=xlAscending, Header:=xlYes 38 End If 39 Set rngList = rngList.CurrentRegion 40 End With 41End Sub 42 43'一覧表にないファイルのフルパスのリストを取得 44Private Function Get_UpdatedSubjectList(ByRef rngList As Range) As Variant 45 Dim WSF As WorksheetFunction 46 Dim myFolder As Folder 47 Dim myFile As File 48 Dim strPath As String 49 Dim strBaseName As String 50 Dim vrtOld As Variant 51 Dim vrtNewList() As Variant 52 Dim ix As Long 53 54 ReDim vrtNewList(1 To 50000) 55 Set WSF = Application.WorksheetFunction 56 '入力済みの件名リスト 57 vrtOld = WSF.Transpose(rngList.Columns(1)) 58 59 strPath = ThisWorkbook.Path 60 61 For Each myFolder In mFSO.GetFolder(strPath).SubFolders 62 For Each myFile In myFolder.Files 63 strBaseName = mFSO.GetBaseName(myFile.Path) 64 If IsError(Application.Match(strBaseName, vrtOld, 0)) Then 65 ix = ix + 1 66 vrtNewList(ix) = myFile.Path 67 End If 68 Next 69 Next 70 71 ReDim Preserve vrtNewList(1 To ix) 72 Get_UpdatedSubjectList = vrtNewList 73End Function 74 75'データの転記 76Private Sub SetUpdated(ByVal vrtSubjectList As Variant, ByRef rngList As Range) 77 Dim c As Range 78 Dim f As Variant 79 Dim strContent As String 80 Dim ix As Long 81 Dim vrtNewList() As Variant 82 ReDim vrtNewList(LBound(vrtSubjectList) To UBound(vrtSubjectList), 1 To 4) 83 84 For Each f In vrtSubjectList 85 ix = ix + 1 86 With Workbooks.Open(f, 0) 87 strContent = .Worksheets(1).Range("C5").Value 88 .Close False 89 End With 90 91 With mFSO 92 vrtNewList(ix, 1) = .GetBaseName(f) 93 vrtNewList(ix, 3) = .GetFile(f).ParentFolder.Name 94 End With 95 vrtNewList(ix, 4) = strContent 96 Next 97 98 With rngList 99 Set rngList = .Cells(.Rows.Count + 1, 1).Resize(UBound(vrtNewList, 1), UBound(vrtNewList, 2)) 100 End With 101 rngList.Value = vrtNewList 102 103 ix = 0 104 For Each c In rngList.Columns(1).Cells 105 ix = ix + 1 106 rngList.Worksheet.Hyperlinks.Add _ 107 Anchor:=c, _ 108 Address:=vrtSubjectList(ix), _ 109 ScreenTip:="クリックでリンクを開きます。", _ 110 TextToDisplay:=c.Value 111 Next 112End Sub

投稿2020/04/05 03:13

mattuwan

総合スコア2163

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

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

momo2134

2020/04/22 10:30 編集

@mattuwanさん 遅くなり申し訳ございません。ご回答いただきありがとうございます。 まさに私がやりたかったことを、ご記載いただきありがとうございます。 いただいたソースコードを拝見し、調べながらも理解しようとしているのですが、 (mattuwanさんにいただいたサンプルコードを元に、マクロを作成しようとしております) 私の知識不足もあり、"データの転記"のプロシージャ内の処理が理解できずにいます。 処理の概要を簡単にご説明いただくことは可能でしょうか?宜しくお願い致します。
mattuwan

2020/04/22 11:33

え? 1)渡されたリストをリストが無くなるまで繰り返し順次見て行く 2)指定のセルの値を変数に記録 3)ファイルの拡張子抜きの名前を変数に記録 4)親フォルダーのパスを変数に記録 5)1へ戻る 6)変数に入れたものを、セルに転記 7)転記したセルの1列目にハイパーリンクを順次設定 というような内容です。 ステップインで実行しながら、ローカルウィンドウで変数の中身を確認してみたください。 参考URL>> http://www.ken3.org/vba/excel-help.html ええと、 セルを一個づつ大量に読み書きするとすごく時間がかかります。 (1つのセルに書きこむのに0.01秒かかったとしても、 10000個のセルに書きこむと100秒かかりますよね?) なので、結果を一旦変数に溜めておき、 最後に1回でセルに転記しています。 参考URL>> http://officetanaka.net/excel/vba/speed/s11.htm https://vbabeginner.net/vba%E3%81%A7%E4%BA%8C%E6%AC%A1%E5%85%83%E9%85%8D%E5%88%97%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E9%AB%98%E9%80%9F%E3%81%AB%E3%82%BB%E3%83%AB%E3%81%B8%E8%B2%BC%E3%82%8A%E4%BB%98%E3%81%91/ なんかいいサイトが簡単にみつからないな^^; 根気強く探してみてください。 求める情報を探せるようになると、回答を待つ時間がなくなるので、 開発がスムーズに進むと思います。
guest

0

うーん...簡易的に30KBのxlsxファイル×1,110個でテストしてみて250secほど。
(旧環境Core i7/3.10GHz、メモリ4GB、Win7pro32bit、Excel2010で)
やっぱり厳しいなぁ

VBA

1Sub test1() 2 Dim Fold As String 3 With Application.FileDialog(msoFileDialogFolderPicker) 4 If .Show = True Then 5 Fold = .SelectedItems(1) 6 Else 7 Exit Sub 8 End If 9 End With 10 11 Const cx = 100 '列数 12 Dim fso As Object 'FileSystemObject 13 Dim f As Object 'file 14 Dim ws As Worksheet 15 Dim wrk As Worksheet 16 Dim i As Long 17 Dim cnt As Long 18 Dim tgFol(2) As String 19 Dim ret(1 To 3000, 1 To cx) 20 21 Dim t As Single: t = Timer 22 23 With Application 24 .ScreenUpdating = False 25 .EnableEvents = False 26 .Calculation = xlCalculationManual 27 End With 28 29 tgFol(0) = Fold & "\10 未対応" 30 tgFol(1) = Fold & "\20 対応中" 31 tgFol(2) = Fold & "\30 対応済み" 32 33 Set fso = CreateObject("Scripting.FileSystemObject") 34 Dim j As Long 35 For i = 0 To 2 36 For Each f In fso.GetFolder(tgFol(i)).Files 37 If f.Name Like "*お客様問い合わせファイル*" Then 38 cnt = cnt + 1 39 With Workbooks.Open(f.Path, UpdateLinks:=False, ReadOnly:=True) 40 On Error Resume Next 41 Set ws = .Sheets("問い合わせ") 42 On Error GoTo 0 43 ret(cnt, 1) = f.Path 44 If Not ws Is Nothing Then 45 ret(cnt, 2) = ws.Range("B2").Value 46 ret(cnt, 3) = ws.Range("B3").Value 47 ret(cnt, 4) = ws.Range("B4").Value 48 ret(cnt, 5) = ws.Range("B5").Value 49 ret(cnt, 6) = ws.Range("B6").Value 50 ret(cnt, 7) = ws.Range("B7").Value 51 ret(cnt, 8) = ws.Range("B8").Value 52 ret(cnt, 9) = ws.Range("B9").Value 53 ':ダミーなのでテスト的にセット(並びが規則的ならLoopできる) 54 With ws.Range("C1:E50") 55 For j = 10 To 99 56 ret(cnt, j) = .Item(j).Value 57 Next 58 End With 59 ret(cnt, 100) = ws.Range("F10").Value 60 End If 61 .Close False 62 End With 63 End If 64 Next f 65 Next 66 'とりあえず新規Sheetに書き出し 67 ThisWorkbook.Sheets.Add.Range("A2").Resize(cnt, cx).Value = ret 68 69 With Application 70 .Calculation = xlCalculationAutomatic 71 .EnableEvents = True 72 .ScreenUpdating = True 73 End With 74 75 Debug.Print cnt, Timer - t 76End Sub

可能であれば、処理時間は早ければ早い方がよく、
配列に取り込む + 書き込むファイル数も絞る ができればよいと考えているのですが、

この方針がいいですよねぇ...
ステータス毎にフォルダを移動したとしても、元のファイル名がユニークなら、
最初に一覧表に書き込む時にファイル名も記録しておくようにしませんか
そうすると、「更新」であれ「新規登録」であれ、そのファイル名でピンポイントに開いて処理すれば良いと思うんですよね
直前に子フォルダ含めたファイルリストをまず作って、それを参照すれば良いと思いますし。


(追記)
> 問い合わせ毎のファイルはユニークなファイル名なので、
..という事であれば
> 直前に子フォルダ含めたファイルリストをまず作って、それを参照すれば良いと思いますし。
のサンプル

VBA

1Sub sample() 2 Dim Fold As String 3 4 With Application.FileDialog(msoFileDialogFolderPicker) 5 If .Show = True Then 6 Fold = .SelectedItems(1) 7 Else 8 Exit Sub 9 End If 10 End With 11 12 Dim tgFol(2) As String 13 14 tgFol(0) = Fold & "\10 未対応" 15 tgFol(1) = Fold & "\20 対応中" 16 tgFol(2) = Fold & "\30 対応済み" 17 18 Dim fso As Object 'Scripting.FileSystemObject 19 Dim f As Object 'file 20 Dim dic As Object 'Scripting.dictionary 21 Dim i As Long 22 23 Set fso = CreateObject("Scripting.FileSystemObject") 24 Set dic = CreateObject("Scripting.Dictionary") 25 '3フォルダ全ファイルからユニークファイル名をkeyにして _ 26 ファイルフルパスをdictionaryに登録する 27 For i = 0 To 2 28 For Each f In fso.GetFolder(tgFol(i)).Files 29 If f.Name Like "*お客様問い合わせファイル*" Then 30 dic(f.Name) = f.Path 31 End If 32 Next 33 Next 34 '↑ここまでは少し時間かかる 35 36 Dim key As String 37 Dim rng As Range 38 Dim r As Range 39 Dim ret(1 To 69) 40 41 With ActiveSheet 'とかThisWorkbook.Sheets("data")とか 42 Set rng = .Range("AQ12", .Cells(.Rows.Count, "AQ").End(xlUp)) 43 End With 44 45 For Each r In rng 46 If r.Value = "" Then 47 'ユニークファイル名がr.RowのA列にある場合 48 key = r.EntireRow.Range("A1").Value 49 '例えば別シートのrと同じ行にあるなら 50 'key = ThisWorkbook.Sheets("一覧").Cells(r.Row, "A").Value 51 52 'dic(key)でフルパスを取り出す 53 With Workbooks.Open(dic(key), UpdateLinks:=False, ReadOnly:=True) 54 With .Sheets("問い合わせ") 55 '1×69の配列にデータセット 56 ret(1) = .Range("AG10").Value 57 ret(2) = .Range("AH10").Value 58 ': 59 ': 60 ': 61 ret(69) = .Range("AT2").Value 62 End With 63 .Close False 64 End With 65 'Value = ""条件の rの行のB列から右に69セル拡張した範囲に書き込み 66 r.EntireRow.Range("B1").Resize(, 69).Value = ret 67 Else 68 ': 69 ': 70 End If 71 Next 72 73End Sub

(2020.04.04追加)
新規登録を追加
「dic.keys()にあって、ファイルリストにないもの」を追加すればいいので、
Match関数でまとめて配列CheckしてLoop時エラー値のものを処理します
処理内容は更新と同じなのでサブプロシージャにして外出し。

VBA

1'--------------------------------------------------------------------- 2Sub sample2() 3 Dim Fold As String 4 Fold = ThisWorkbook.Path 'とかActiveWorkbook.Pathとか 5 6 Dim tgFol(2) As String 7 8 tgFol(0) = Fold & "\10 未対応" 9 tgFol(1) = Fold & "\20 対応中" 10 tgFol(2) = Fold & "\30 対応済み" 11 12 Dim fso As Object 'Scripting.FileSystemObject 13 Dim f As Object 'file 14 Dim dic As Object 'Scripting.dictionary 15 Dim i As Long 16 17 Set fso = CreateObject("Scripting.FileSystemObject") 18 Set dic = CreateObject("Scripting.Dictionary") 19 '3フォルダ全ファイルからユニークファイル名をkeyにして _ 20 ファイルフルパスをdictionaryに登録する 21 For i = 0 To 2 22 For Each f In fso.GetFolder(tgFol(i)).Files 23 If f.Name Like "*お客様問い合わせファイル*" Then 24 dic(f.Name) = f.Path 25 End If 26 Next 27 Next 28 '↑ここまでは少し時間かかる 29 30 Dim key As String 31 Dim rng As Range 32 Dim r As Range 33 34 With ActiveSheet 'とかThisWorkbook.Sheets("data")とか 35 Set rng = .Range("AQ12", .Cells(.Rows.Count, "AQ").End(xlUp)) 36 End With 37 38 For Each r In rng 39 If r.Value = "" Then 40 'ユニークファイル名がr.RowのA列にある場合 41 key = r.EntireRow.Range("A1").Value 42 '例えば別シートのrと同じ行にあるなら 43 'key = ThisWorkbook.Sheets("一覧").Cells(r.Row, "A").Value 44 45 'dic(key)でフルパスを取り出してサブプロシージャへ 46 Call wkGetdata(dic(key), r) 47 Else 48 ': 49 ': 50 End If 51 Next 52 53 '新規登録チェック 54 Dim chk, buf 55 chk = Application.Match(dic.keys(), rng.EntireRow.Columns(1), 0) 56 '新規書き出し位置 57 Set r = rng.Offset(rng.Count).Item(1) 58 For i = 1 To UBound(chk) 59 If IsError(chk(i)) Then 60 key = dic(dic.keys()(i)) 61 'フォルダによって除外するなら条件分岐させる 62 'buf = Split(key, "\") 63 'If buf(5) <> "30 対応済み" Then 64 Call wkGetdata(key, r) 65 'ファイル名も忘れずに追加 66 r.EntireRow.Range("A1").Value = dic.keys()(i) 67 Set r = r.Offset(1) 68 'End If 69 End If 70 Next 71 72End Sub 73'--------------------------------------------------------------------- 74Sub wkGetdata(fName As String, r As Range) 75 Dim ret(1 To 69) 76 With Workbooks.Open(fName, UpdateLinks:=False, ReadOnly:=True) 77 With .Sheets("問い合わせ") 78 '1×69の配列にデータセット 79 ret(1) = .Range("AG10").Value 80 ret(2) = .Range("AH10").Value 81 ': 82 ': 83 ': 84 ret(69) = .Range("AT2").Value 85 End With 86 .Close False 87 End With 88 'Value = ""条件の rの行のB列から右に69セル拡張した範囲に書き込み 89 r.EntireRow.Range("B1").Resize(, 69).Value = ret 90End Sub 91'---------------------------------------------------------------------

変数を使い回ししてるので解り難ければ適宜変更してください

投稿2020/04/02 09:13

編集2020/04/04 11:02
end-u

総合スコア52

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

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

momo2134

2020/04/03 06:25

@end-uさん サンプルソースを作成いただきありがとうございます。 問い合わせ毎のファイルはユニークなファイル名なので、 一覧の別シートに取り込むファイル名の一覧を記録する方式でも問題ないです。 いただいたサンプルソースを拝見して、 ファイル間の転記方法がすっきりしていてかなり参考になりました。 ありがとうございます。
momo2134

2020/04/04 09:57 編集

@end-uさん サンプルを追記いただきありがとうございます。 確認させてただき、以下いくつかご質問させていただきたいことがあります。 処理の概要として下記のように理解しました。 1:一覧ファイル・子フォルダが格納されているフォルダを選択させる 2:3つの子フォルダ内全ファイルのフルパスをdictionaryシートに登録 3:一覧シートのAQ列がnullの場合、その行のA列をキーに、dictionaryシートのフルパスから対象ファイルを開く 4:ファイルのデータを配列に格納 5:配列に格納したデータを一覧の対象の行にB1から順にデータを書き込む 6:3~5を一覧のレコード件数分繰り返す 【質問】 Q1) 1のフォルダを選択する作業をなくし、マクロを実行するとdictionaryシートに自動で2の処理を行なうことは可能でしょうか? Q2) 一覧上にはないが、子フォルダ内には新しく書き込みたいファイルがある場合(問い合わせファイルは日々増えるため) 3での条件を増やすことで実現可能でしょうか? マクロの修正が必要になってからVBAの勉強を始めていますが、知識が追いついていない部分もあり、 ご質問ばかりになってしまい、申し訳ございません。 宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問