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

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

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

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

Q&A

解決済

3回答

293閲覧

VBA 照合集計転記

dra_naka

総合スコア3

VBA

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

0グッド

1クリップ

投稿2024/05/02 06:28

実現したいこと

・実績.xlsmから以下内容が実行されるマクロを作成したい。
ステップ1:実績.xlsmから生産性_全体.xlsxへ転記
ステップ2:予算書.xlsxから生産性_全体.xlsxへ転記
イメージ説明

前提

・実績.xlsm、予算書.xlsx、生産性_全体.xlsxは同一階層フォルダに保管
・実績.xlsm
・製番、型式は同一名称を1つに集約
・開始月日は、同一製番で一番古い年月日を1つ抽出
※イメージ図の場合、セルC2は2024/3/13になります。
・完了月日は、同一製番で一番新しい年月日を1つ抽出
※イメージ図の場合、セルD2は2024/3/29になります。
イメージ図上、2024/3/29は隠れています。
・作業時間[min]は、同一製番の場合合算
※イメージ図の場合、セルF2は2835となります。
・目標[min]は、同一型式の場合合算
※イメージ図の場合、セルE2は1800となります。
・実績.xlsmのデータが今後増えるため、高速処理できるコードを目指したいです。

該当のソースコード

VBA

1Sub 照合転記() 2 Dim Swb As Workbook '実績ファイル 3 Dim Sws As Worksheet '実績ファイル実績シート 4 Dim Cwb As Workbook '生産性_全体ファイル 5 Dim Cws As Worksheet '生産性_全体ファイルSheet1シート 6 Dim Ywb As Workbook '予算ファイル 7 Dim Yws As Worksheet '予算ファイルDBシート 8 Dim lastRowS As Long 9 Dim lastRowC As Long 10 Dim RowTC As Long 11 Dim i As Long 12 Dim j As Long 13 Dim Mydic As Object 14 Dim Mykeys, Myitems 15 16 Set Swb = ThisWorkbook 17 Set Sws = Swb.Sheets("実績") 18 19 Set Cwb = Workbooks.Open(ThisWorkbook.Path & "\生産性_全体.xlsx") ' 生産性_全体.xlsxを開く 20 Set Cws = Cwb.Sheets("Sheet1") 21 Cws.Range(Cws.Range("A2"), Cws.Range("A" & Cws.Cells.Rows.Count)).EntireRow.Delete '2行目以降を削除 22 23 lastRowS = Sws.Cells(Sws.Rows.Count, "A").End(xlUp).Row ' 実績の最終行を取得 24 lastRowC = Cws.Cells(Cws.Rows.Count, "A").End(xlUp).Row ' 生産性_全体の最終行を取得 25 26 Set Mydic = CreateObject("Scripting.Dictionary") 27 On Error Resume Next 28 For i = 6 To lastRowS 29 If Not IsEmpty(Sws.Cells(i, 1).Value) Then 30 Mydic.Add Sws.Cells(i, 1).Value, Sws.Cells(i, 2).Value 31 End If 32 Next i 33 On Error GoTo 0 34 35 For j = 0 To Mydic.Count - 1 36 Mykeys = Mydic.Keys 37 Myitems = Mydic.Items 38 Cws.Cells(j + 2, 1).Value = Mykeys(j) 39 Cws.Cells(j + 2, 2).Value = Myitems(j) 40 Next j 41 42 Set Mydic = Nothing 43 44 Cwb.Save 45 Cwb.Close 46End Sub 47 48

試したこと

・連想配列を見様見真似で作成しましたが、転記したいアイテムが複数ある事と、
各項目毎に条件があり、解決方法が分からなくご相談させていただきました。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

sk.exe

2024/05/02 07:34 編集

・[実績テーブル]と[予算書テーブル]を互いの[型式]と[作業]同士をキーとして内部結合。 ・更に[実績テーブル]の[製番]と[型式]でグループ化し、[実績テーブル]の[開始月日]の最小値、[実績テーブル]の[完了月日]の最大値、[実績テーブル]の[作業時間]の合計、[予算書テーブル]の[目標]の合計をグループごとに集計した結果を得る。 ・上記の結果を[製番]の昇順に並べ替える。 といったデータベースシステムにおける集計クエリのような処理を行ないたいのであれば、Power Query を使用された方がよろしいのではないかと。
tatsu99

2024/05/02 09:23

1.同一製番の場合、型式は必ず同じですか。 例 製番=10001,型式=A   製番=10001,型式=Y のようなケースは存在しないと考えて良いですか。 2.実績.xlsmのデータが今後増えるということですが、最大、何行程度になることを想定していますか。 (提示例では38行)
dra_naka

2024/05/02 09:42

sk.exeさん powerqueryでの提案ありがとうございます。 勝手ながら、今回はVBAで達成したいと考えていますので、別の機会で勉強させていただきます。 tatsu99さん 連絡ありがとうございます。 以下回答させていただきます。 1.につきまして、同一製番の場合、型式は必ず同じになります。 2.につきまして、Max8千行を想定しております。
tatsu99

2024/05/02 10:14

あなたが提示されたマクロを修正しようとしていますが、不明点です。 予算書.xlsxは、実績.xlsmと同じフォルダにあると解釈して良いですか。 又、予算書.xlsxのシート名は何でしょうか。
dra_naka

2024/05/02 12:07

対応ありがとうございます。 情報が足りておらず申し訳ありません。 ・予算書.xlsxは、実績.xlsmと同じフォルダになります。 ・予算書.xlsxのシート名はDBとなります。
hatena19

2024/05/02 21:56 編集

エクセルのバージョンはなんでしょうか。365なら関数でできそうです。 あと、画像をみると各データはテーブル化されているようにみえますが、テーブル化されていますか。
dra_naka

2024/05/03 13:37

hatena19さん連絡ありがとうございます。 Excelのバージョンは、Home&business2021になります。 各データはテーブル化しておりません。
guest

回答3

0

既に解決済みですが、質問コメントでバージョンの連絡をいただいたので、関数を利用した方法を紹介しておきます。
バージョンが2021とのことなので、UNIQUE関数が使えますのでそれを使うと簡単です。
テーブル化はしてないとのことですが、テーブル化すると記述が簡単になるので、テーブル化されることを推奨します。
実績ブックの実績シートの表をテーブル化して名前を「実績表」
予算書ブックのDBシートの表をテーブル化して名まえを「予算表」とします。

生産性_全体ブックのSheet1の1行目には、A1から下記のように項目名が入力ずみとします。

製番 型式 開始月日 完了月日 目標[min] 作業時間[min]
各セルに下記のように式を設定します。

A2セル: =UNIQUE(実績.xlsm!実績表[[製番]:[型式]])

C2セル: =MINIFS(実績.xlsm!実績表[開始月日],実績.xlsm!実績表[製番],Sheet1!A2:A20)

D2セル: =MAXIFS(実績.xlsm!実績表[完了月日],実績.xlsm!実績表[製番],Sheet1!A2:A20)

E2セル: =SUMIF(予算書.xlsx!予算表[型式],B2:B20,予算書.xlsx!予算表[目標'[min']])

F2セル: =SUMIF(実績.xlsm!実績表[型式],B2:B20,実績.xlsm!実績表[作業時間'[min']])

上記で20の部分はA:B列のスピル後の行数になります。

式ではなく値として保存しておきたいという場合は、VBAで式を設定後、 式を値に変化すればいいでしょう。

vba

1Sub 照合転記() 2 Application.ScreenUpdating = False '画面更新停止 3 Application.Calculation = xlCalculationManual '手動計算 4 5 Dim Swb As Workbook '実績ファイル 6 Dim Cwb As Workbook '生産性_全体ファイル 7 Dim Ywb As Workbook '予算ファイル 8 Set Swb = ThisWorkbook 9 Set Ywb = Workbooks.Open(ThisWorkbook.Path & "\予算書.xlsx") 10 Set Cwb = Workbooks.Open(ThisWorkbook.Path & "\生産性_全体.xlsx") 11 12 With Cwb.Worksheets("Sheet1") 13 .Range("A2").CurrentRegion.Offset(1).ClearContents '以前のデータがあれば削除 14 .Range("A2").Formula2 = "=UNIQUE(実績.xlsm!実績表[[製番]:[型式]])" 15 Dim rcnt As Long: rcnt = .Range("A1").CurrentRegion.Rows.Count 16 .Range("C2").Formula2 = "=MINIFS(実績.xlsm!実績表[開始月日],実績.xlsm!実績表[製番],Sheet1!A2:A" & rcnt & ")" 17 .Range("D2").Formula2 = "=MAXIFS(実績.xlsm!実績表[完了月日],実績.xlsm!実績表[製番],Sheet1!A2:A" & rcnt & ")" 18 .Range("E2").Formula2 = "=SUMIF(予算書.xlsx!予算表[型式],B2:B" & rcnt & ",予算書.xlsx!予算表[目標'[min']])" 19 .Range("F2").Formula2 = "=SUMIF(実績.xlsm!実績表[型式],B2:B" & rcnt & ",実績.xlsm!実績表[作業時間'[min']])" 20 With .Range("A1").CurrentRegion 21 .Value = .Value '式の出力結果を値に変換 22 End With 23 End With 24 25 Ywb.Close 26 Cwb.Save 27 Cwb.Close 28 29 Application.ScreenUpdating = True 30 Application.Calculation = xlCalculationAutomatic '自動計算 31End Sub

投稿2024/05/04 07:06

hatena19

総合スコア33795

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

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

dra_naka

2024/05/04 14:21

コード作成ありがとうございます。 ベストアンサーのコード理解後、試させていただきます。
guest

0

連想配列で保持したいアイテムが複数ある場合は、1つだけに絞ります。
今回では、生産性_全体ファイルSheet1シートの行番号を保持します。
予算書のDBの連想配列は、型式+作業がキーになります。保持したいアイテムは1つなので、目標時間を保持します。
念のため型式と作業の間に"|"をいれて記憶します。
これは、例えば氏名をキーにする場合、苗字+名前にすると、
時任 三郎 と 時 任三郎 は、両方とも、時任三郎になり、同じキーになってしまいます。
これを時任|三郎 と 時|任三郎 とにすれば、区別することができます。
このため、各項目の間に"|"を入れてキーを作成します。
以下が、マクロになります。

VBA

1Option Explicit 2 3Sub 照合転記() 4 Dim Swb As Workbook '実績ファイル 5 Dim Sws As Worksheet '実績ファイル実績シート 6 Dim Cwb As Workbook '生産性_全体ファイル 7 Dim Cws As Worksheet '生産性_全体ファイルSheet1シート 8 Dim Ywb As Workbook '予算ファイル 9 Dim Yws As Worksheet '予算ファイルDBシート 10 Dim lastwrowS As Long 11 Dim lastwrowC As Long 12 Dim lastwrowY As Long 13 Dim wrowY As Long 14 Dim wrowS As Long 15 Dim wrowC As Long 16 Dim Mydic As Object 17 Dim YwDic As Object 18 Dim mykey As String 19 Dim keyY As String 20 Set Swb = ThisWorkbook 21 Set Sws = Swb.Sheets("実績") 22 Set Ywb = Workbooks.Open(ThisWorkbook.Path & "\予算書.xlsx") 23 Set Yws = Ywb.Sheets("DB") 24 lastwrowY = Yws.Cells(rowS.Count, "A").End(xlUp).Row ' 予算の最終行を取得 25 Set YwDic = CreateObject("Scripting.Dictionary") 26 '予算書読込&キー登録 27 For wrowY = 2 To lastwrowY 28 keyY = Yws.Cells(wrowY, "A").Value & "|" & Yws.Cells(wrowY, "B").Value 29 YwDic(keyY) = Yws.Cells(wrowY, "C").Value 30 Next 31 Ywb.Close 32 33 Set Cwb = Workbooks.Open(ThisWorkbook.Path & "\生産性_全体.xlsx") ' 生産性_全体.xlsxを開く 34 Set Cws = Cwb.Sheets("Sheet1") 35 Cws.rowS("2:" & rowS.Count).ClearContents 36 37 lastwrowS = Sws.Cells(rowS.Count, "A").End(xlUp).Row ' 実績の最終行を取得 38 lastwrowC = 2 39 40 Set Mydic = CreateObject("Scripting.Dictionary") 41 For wrowS = 6 To lastwrowS 42 mykey = Sws.Cells(wrowS, "A").Value 43 If Mydic.exists(mykey) = False Then 44 wrowC = lastwrowC 45 Cws.Cells(wrowC, "A").Value = Sws.Cells(wrowS, "A").Value '製番 46 Cws.Cells(wrowC, "B").Value = Sws.Cells(wrowS, "B").Value '型式 47 Cws.Cells(wrowC, "C").Value = Sws.Cells(wrowS, "E").Value '開始年月日 48 Cws.Cells(wrowC, "D").Value = Sws.Cells(wrowS, "G").Value '完了年月日 49 Cws.Cells(wrowC, "F").Value = Sws.Cells(wrowS, "J").Value '作業時間 50 '目標時間 51 keyY = Sws.Cells(wrowS, "B").Value & "|" & Sws.Cells(wrowS, "C").Value 52 If YwDic.exists(keyY) = False Then 53 MsgBox ("予算書.xlsx内に" & keyY & "が存在しない①") 54 Exit Sub 55 End If 56 Cws.Cells(wrowC, "E").Value = YwDic(keyY) '目標 57 Mydic(mykey) = lastwrowC 58 lastwrowC = lastwrowC + 1 59 Else 60 wrowC = Mydic(mykey) 61 '開始年月日更新 62 If Sws.Cells(wrowS, "E").Value < Cws.Cells(wrowC, "C").Value Then 63 Cws.Cells(wrowC, "C").Value = Sws.Cells(wrowS, "E").Value 64 End If 65 '完了年月日更新 66 If Sws.Cells(wrowS, "G").Value > Cws.Cells(wrowC, "D").Value Then 67 Cws.Cells(wrowC, "D").Value = Sws.Cells(wrowS, "G").Value 68 End If 69 '作業時間加算 70 Cws.Cells(wrowC, "F").Value = Cws.Cells(wrowC, "F").Value + Sws.Cells(wrowS, "J").Value 71 '目標加算 72 keyY = Sws.Cells(wrowS, "B").Value & "|" & Sws.Cells(wrowS, "C").Value 73 If YwDic.exists(keyY) = False Then 74 MsgBox ("予算書.xlsx内に" & keyY & "が存在しない②") 75 Exit Sub 76 End If 77 Cws.Cells(wrowC, "E").Value = Cws.Cells(wrowC, "E").Value + YwDic(keyY) 78 End If 79 Next 80 Cwb.Save 81 Cwb.Close 82 MsgBox ("完了") 83End Sub 84

追伸:
約8000行で試験しましたが10秒ほどで終わりましたので、実用上問題ないと考えます。

投稿2024/05/02 13:38

tatsu99

総合スコア5470

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

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

dra_naka

2024/05/03 01:28

コード作成ありがとうございます。 すごく速く処理でき、ほぼ解決出来ています。 一点だけ、生産性_全体.xlsxの目標[min]値が得たい結果(イメージ画像)と違いが出てしまいます。 原因わかりますでしょうか? 得たい結果:    今回の結果:  2行目|1800     2行目|3800  3行目|1880     3行目|3040
tatsu99

2024/05/03 02:02

製番=10001の目標ですが、表示されている範囲だけに限定すると、  行 製番 型式 作業  目標(min) 6行 10001 A  サブ組1 100 7行 10001 A  サブ組1 100 8行 10001 A  サブ組2 150 9行 10001 A  サブ組2 150 10行 10001 A  サブ組2 150 となるので、これを加算すると、650になります。 これであってますか? 実績.xlsmの型式+作業が、予算書.xlsxの型式+作業に一致する行を検索し、その行の目標(min)を 取得し、その値の合計を生産性_全体.xlsxの目標(min)に出力しています。
dra_naka

2024/05/03 02:24

返信ありがとうございます。 私の言葉足らずで申し訳ありません。 製番10001の場合、予算書.xlsxでは型式A該当(実績.xlsmで10001=Aにより)になります。 ロジックとして、型式は同一で集約し、目標[min]は合算する結果にしたいです。その際作業は考慮せず。 以下のように 型式 目標[min] A   1800 この結果を、生産性_全体.xlsxの目標[min]へ転記としたいです。
tatsu99

2024/05/03 02:33

型式 Aの予算書.xlsxの合計が1800なので、その値を表示すると理解しました。 修正版をアップする迄、しばらくお待ちください。
guest

0

ベストアンサー

修正版です。
予算書のDBの連想配列は、型式のみをキーとしました。値は、その型式の合計値を保持するようにしました。

VBA

1Option Explicit 2 3Sub 照合転記() 4 Dim Swb As Workbook '実績ファイル 5 Dim Sws As Worksheet '実績ファイル実績シート 6 Dim Cwb As Workbook '生産性_全体ファイル 7 Dim Cws As Worksheet '生産性_全体ファイルSheet1シート 8 Dim Ywb As Workbook '予算ファイル 9 Dim Yws As Worksheet '予算ファイルDBシート 10 Dim lastwrowS As Long 11 Dim lastwrowC As Long 12 Dim lastwrowY As Long 13 Dim wrowY As Long 14 Dim wrowS As Long 15 Dim wrowC As Long 16 Dim Mydic As Object 17 Dim YwDic As Object 18 Dim mykey As String 19 Dim keyY As String 20 Set Swb = ThisWorkbook 21 Set Sws = Swb.Sheets("実績") 22 Set Ywb = Workbooks.Open(ThisWorkbook.Path & "\予算書.xlsx") 23 Set Yws = Ywb.Sheets("DB") 24 lastwrowY = Yws.Cells(rowS.Count, "A").End(xlUp).Row ' 予算の最終行を取得 25 Set YwDic = CreateObject("Scripting.Dictionary") 26 '予算書読込&キー登録 27 For wrowY = 2 To lastwrowY 28 keyY = Yws.Cells(wrowY, "A").Value 29 If YwDic.exists(keyY) = False Then 30 YwDic(keyY) = 0 31 End If 32 YwDic(keyY) = YwDic(keyY) + Yws.Cells(wrowY, "C").Value 33 Next 34 Ywb.Close 35 36 Set Cwb = Workbooks.Open(ThisWorkbook.Path & "\生産性_全体.xlsx") ' 生産性_全体.xlsxを開く 37 Set Cws = Cwb.Sheets("Sheet1") 38 Cws.rowS("2:" & rowS.Count).ClearContents 39 40 lastwrowS = Sws.Cells(rowS.Count, "A").End(xlUp).Row ' 実績の最終行を取得 41 lastwrowC = 2 42 43 Set Mydic = CreateObject("Scripting.Dictionary") 44 For wrowS = 6 To lastwrowS 45 mykey = Sws.Cells(wrowS, "A").Value 46 If Mydic.exists(mykey) = False Then 47 wrowC = lastwrowC 48 Cws.Cells(wrowC, "A").Value = Sws.Cells(wrowS, "A").Value '製番 49 Cws.Cells(wrowC, "B").Value = Sws.Cells(wrowS, "B").Value '型式 50 Cws.Cells(wrowC, "C").Value = Sws.Cells(wrowS, "E").Value '開始年月日 51 Cws.Cells(wrowC, "D").Value = Sws.Cells(wrowS, "G").Value '完了年月日 52 Cws.Cells(wrowC, "F").Value = Sws.Cells(wrowS, "J").Value '作業時間 53 '目標時間 54 keyY = Sws.Cells(wrowS, "B").Value 55 If YwDic.exists(keyY) = False Then 56 MsgBox ("予算書.xlsx内に" & keyY & "が存在しない①") 57 Exit Sub 58 End If 59 Cws.Cells(wrowC, "E").Value = YwDic(keyY) '目標 60 Mydic(mykey) = lastwrowC 61 lastwrowC = lastwrowC + 1 62 Else 63 wrowC = Mydic(mykey) 64 '開始年月日更新 65 If Sws.Cells(wrowS, "E").Value < Cws.Cells(wrowC, "C").Value Then 66 Cws.Cells(wrowC, "C").Value = Sws.Cells(wrowS, "E").Value 67 End If 68 '完了年月日更新 69 If Sws.Cells(wrowS, "G").Value > Cws.Cells(wrowC, "D").Value Then 70 Cws.Cells(wrowC, "D").Value = Sws.Cells(wrowS, "G").Value 71 End If 72 '作業時間加算 73 Cws.Cells(wrowC, "F").Value = Cws.Cells(wrowC, "F").Value + Sws.Cells(wrowS, "J").Value 74 End If 75 Next 76 Cwb.Save 77 Cwb.Close 78 MsgBox ("完了") 79End Sub 80

投稿2024/05/03 02:45

tatsu99

総合スコア5470

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

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

dra_naka

2024/05/03 13:35

コード作成ありがとうございます。 凄いです!!狙い通り動作する事ができました。 連想配列の可能性を感じる事ができました、私自身コードを理解出来ていないので読み解けるように進めていきます。 連想配列を理解するコツがあれば教えていただけないでしょうか? キーとアイテムの関係性が頭の中でぐるぐる回ってしまいます。
tatsu99

2024/05/04 00:43

>連想配列を理解するコツがあれば教えていただけないでしょうか? いくつかの問題をといて、慣れるしかありません。 まずは、このコードを読んで理解するようにしてください。 そのうえで、不明点があれば、補足してください。
dra_naka

2024/05/04 06:32

返信ありがとうございます。 はい、こちらのコードの理解に励んでいきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問