[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『マクロで各シートの表を条件に沿ってファイルを分割したい』(ぽぽ)
表題の件、教えてください。
マクロが入っている「設定」シートのA2セルに記載しているファイルに対して、下記処理を実施したいです。
複数シートにフォームの同じ表があり、「まとめ」シート以外の全シートが対象で、A列の「名前」ごとに書式を保持したままファイルを分割したいです。
分割したファイルを保存するフォルダパスは、マクロが入っている「設定」シートのA1セルに記載しています。
イメージは、1行目の項目行は固定で、名前がaさんなら、aさんだけの表にして、指定フォルダにファイルを分割して保存したいです。何列目まであるかは分からないので、表全体を残す感じにしたいです。
分割したファイル名は、aさんなら、「aさん_まとめ.xlsx」としたい。
例えば、aさんが1つのシートにしか存在しない場合がありますが、その場合は該当がないシートには項目行だけ表示させて、aさんのファイルも生成するようにしたいです。(つまり、どこかのシートに存在する名前全て重複除いてファイル生成したいです)
イメージです。 A1セルから表が始まっており、これが複数シートあります。
名前	項目1	項目2	項目3
aさん	1	a	aa
aさん	2	b	bb
bさん	3	c	cc
cさん	4	d	dd
cさん	5	e	ee
< 使用 Excel:Microsoft365、使用 OS:Windows10 >
    |[A]  |[B]  |[C]  |[D]  
 [1]|名前 |項目1|項目2|項目3
 [2]|aさん|    1|a    |aa   
 [3]|aさん|    2|b    |bb   
 [4]|bさん|    3|c    |cc   
 [5]|cさん|    4|d    |dd   
 [6]|cさん|    5|e    |ee   
整理のお手伝いだけども。。。^^
0.指定のブック開く
1.全シート読み込んで名前マスタ作成
2.マスタをキーに名前を加工して、名前を変えて保存
3.保存先のブックで当該シートをマスタでフイルタしたものに置き換え
4.全シート3.の処理して保存
5.2〜4全マスタ分繰り返す
6.後始末して御終い
いろいろ。。。様々な方法が有るとはぞんじますが↑
みたいなことでも出来るかもしれませんですね( ̄▽ ̄) ←かなりいいかげん^^;
m(__)m
(隠居Z) 2024/04/13(土) 10:48:02
うまくいかないのは3点です。(もっと違うやり方で良い方法あれば教えて頂けると勉強になります)
1.フィルタ掛かった状態で保存されていますが、不要なデータは削除して必要な名前の人のみ残したい。
2.例えばaさんは1つのシートしか該当ない場合もシート作成して項目名だけ残したいのですが、このコードだと名前が登場するシートのみしか生成されません。
3.マクロを再度稼働したときに毎回既にファイルがいますとメッセージでてしまうので、上書きするような設定にしたい。
Sub 分割()
    Dim 分割元 As Workbook
    Dim folderPath As String
    Dim i As Long
    Dim ws As Worksheet
    Dim MyDic As Object
    Dim tmp As Variant
    Dim newWb As Workbook
    Dim wsIndex As Long
    Set MyDic = CreateObject("Scripting.Dictionary")
    ' 分割元のファイルを読み取り専用で開く
    Set 分割元 = Workbooks.Open(Filename:=ThisWorkbook.Sheets("設定").Range("A2").Value, ReadOnly:=True)
    ' フォルダパスを取得
    folderPath = ThisWorkbook.Sheets("設定").Range("A1").Value
    Application.ScreenUpdating = False
    ' 名前の一覧を取得
    For Each ws In 分割元.Sheets
        If ws.name <> "まとめ" Then
            For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                If Not MyDic.Exists(ws.Cells(i, 1).Value) Then MyDic.Add ws.Cells(i, "A").Value, ""
            Next i
        End If
    Next ws
    ' 各名前ごとにファイルを作成
    For Each tmp In MyDic.keys
        Set newWb = Workbooks.Add
        wsIndex = 1
        For Each ws In 分割元.Sheets
            If ws.name <> "まとめ" Then
                ws.AutoFilterMode = False
                ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp
                If Application.WorksheetFunction.Subtotal(103, ws.Columns(1)) > 1 Then
                    ws.Copy After:=newWb.Sheets(wsIndex)
                    wsIndex = wsIndex + 1
                End If
            End If
        Next ws
        ' 新しいブックの保存
        newWb.SaveAs folderPath & "\" & tmp & "_まとめ.xlsx"
        newWb.Close SaveChanges:=True
    Next tmp
    ' 分割元のファイルを閉じる
    分割元.Close SaveChanges:=False
    Application.ScreenUpdating = True
End Sub
(ぽぽ) 2024/04/13(土) 18:16:54
>3.マクロを再度稼働したときに毎回既にファイルがいますとメッセージでてしまうので、上書きするような設定にしたい。
については、下記のように最初にフォルダを削除する方法で解決いたしました。
   ' 既存のファイルがあれば削除する
    On Error Resume Next
    Kill folderPath & "\" & "*.xlsx"
    On Error GoTo 0
下記2点については、色々やってみるもどうしても上手くいきませんので、引き続きアドバイス頂けますと助かります。よろしくお願いいたします。
1.フィルタ掛かった状態で保存されていますが、不要なデータは削除して必要な名前の人のみ残したい。
2.例えばaさんは1つのシートしか該当ない場合もシート作成して項目名だけ残したいのですが、このコードだと名前が登場するシートのみしか生成されません。
(ぽぽ) 2024/04/14(日) 07:14:40
横入失礼。 | 1.フィルタ掛かった状態で保存されていますが、不要なデータは削除して必要な名前の人のみ残したい。
     ws.Copy After:=newWb.Sheets(wsIndex)
     これはシートコピーなので、不要データもそのままコピーされますよね。
     そうではなく、セル範囲をコピーするようにしてください。
     フィルタを掛ける前に、見出しを除くデータ部分のセル範囲を求めておき、
     フィルタを掛けた後で、その範囲を普通にコピーペイストすれば、
     フィルタが掛かったデータだけがコピーされる仕様ですので、これを利用します。
 | 2.例えばaさんは1つのシートしか該当ない場合もシート作成して項目名だけ残したいのですが、
 |   このコードだと名前が登場するシートのみしか生成されません。
     どのかたも、元のブックのシートの数と同じだけのシートが必要なんですか?
     ひとつのシートに、コピーペイストして、特定列に元のシート名を書き込む、という方法のほうがよいかも知れませんね。
     いやそんなことじゃないです、というなら
     ・予め、(見出しを入れた)必要なシート数があるブックをテンプレートとして用意し、
       それを増殖して個人個人別ブックを作り、
     ・それにペイストしていくような作りにしたらいかがですか?
| 3.マクロを再度稼働したときに毎回既にファイルがいますとメッセージでてしまうので、 | 上書きするような設定にしたい。
     Application.DisplayAlerts = False
        (保存処理)
     Application.DisplayAlerts = True
     と言う方法もあります。
(xyz) 2024/04/14(日) 08:06:48
>どのかたも、元のブックのシートの数と同じだけのシートが必要なんですか?
Yesです。
>     ws.Copy After:=newWb.Sheets(wsIndex)
     これはシートコピーなので、不要データもそのままコピーされますよね
 → そういうことだったのですね、、、
     フィルタを掛ける前に、見出しを除くデータ部分のセル範囲を求めておき、
     フィルタを掛けた後で、その範囲を普通にコピーペイストすれば、
     フィルタが掛かったデータだけがコピーされる仕様ですので、これを利用します。
という指定の方法が分からないのですが、具体的に教えていただけますでしょうか。
                ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp                
                ws.Copy After:=newWb.Sheets(wsIndex)
この部分を、
                ws.Autofilter.Rang.AutoFilter Field:=1, Criteria1:=tmp                
                ws.Autofilter.Rang.Copy After:=newWb.Sheets(wsIndex)
のようにやったらどうか等、色々検証するもエラーで解決できません。
また、下記のコードでは、一応名前が該当無しの場合もシート生成されました。不要なシートも1枚目に「Sheet1」と生成されてしまいますが、後で削除すれば何とかやりたいことは実現できそうな気がします、、、無理やりですが。
Sub Test中()
    Dim 分割元 As Workbook
    Dim folderPath As String
    Dim i As Long
    Dim ws As Worksheet
    Dim MyDic As Object
    Dim tmp As Variant
    Dim newWb As Workbook
    Dim wsIndex As Long
    Set MyDic = CreateObject("Scripting.Dictionary")
    ' 分割元のファイルを読み取り専用で開く
    Set 分割元 = Workbooks.Open(Filename:=ThisWorkbook.Sheets("設定").Range("A2").Value, ReadOnly:=True)
    ' フォルダパスを取得
    folderPath = ThisWorkbook.Sheets("設定").Range("A1").Value
    Application.ScreenUpdating = False
    ' 名前の一覧を取得
    For Each ws In 分割元.Sheets
        If ws.name <> "まとめ" Then
            For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                If Not MyDic.Exists(ws.Cells(i, 1).Value) Then MyDic.Add ws.Cells(i, "A").Value, ""
            Next i
        End If
    Next ws
    ' 各名前ごとにファイルを作成
    For Each tmp In MyDic.keys
        Set newWb = Workbooks.Add
        wsIndex = 1
        For Each ws In 分割元.Sheets
            If ws.name <> "まとめ" Then
                ws.AutoFilterMode = False
                ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp
                ws.Copy After:=newWb.Sheets(wsIndex)
                wsIndex = wsIndex + 1
            End If
        Next ws
        ' 新しいブックの保存 (既にファイル存在の場合は上書きするように警告無視)
          Application.DisplayAlerts = False
          newWb.SaveAs folderPath & "\" & tmp & "_まとめ.xlsx"
          newWb.Close SaveChanges:=True
          Application.DisplayAlerts = True
    Next tmp
    ' 分割元のファイルを閉じる
    分割元.Close SaveChanges:=False
    Application.ScreenUpdating = True
End Sub
(ぽぽ) 2024/04/14(日) 09:05:44
私も横からですが何点か。
■1
ループ途中に毎回↓を実行してますが、1回でよいと思います
ws.AutoFilterMode = False
■2
>例えばaさんは1つのシートしか該当ない場合もシート作成して項目名だけ残したい
発想を変えて、
1. まとめシート以外を(新規ブックへ)コピーする(or全シートをコピーしてからまとめシートを削除する) 2. ↑の全シートを巡回してaさん【以外】を抽出して項目行以外を削除する 3. (確認メッセージなしで)ブックを保存する
といったアプローチでもよいと思います。
(もこな2) 2024/04/14(日) 09:16:40
(隠居Z) 2024/04/14(日) 10:45:13
 Option Explicit
Private Sub LetFilter_Ws(ws, mkey)
    With ws
        With .Cells(1)
            .AutoFilter 1, mkey
            .CurrentRegion.Copy .Cells(1, 10)
            .AutoFilter
        End With
        .Range("A:I").Delete
    End With
End Sub
こんな感じ。。。^^;
当該のワークシートと当該のマスターキー「例、"aさん"」を渡してやります
ほんの一例です(*^^*)///
m(__)m
(隠居Z) 2024/04/14(日) 12:12:48
皆さんからコメントありましたので、解決済かもしれませんが、補足説明します。
 見出しも含めてコピーするのであれば、
    Set rng = ws.[A1].CurrentRegion
 としておいて、単に
    rng.Copy newWb.Sheets(wsIndex).[A1]
 で良かったですね。(*)
なお、転記先のシートを順次指定する部分はそちらで適切に対応してください. (つまり、必要なシートを確保する部分ですね。)
(上記の* についての補足) ・通常は、見出しを除く本体部分だけをコピーするケースが多く、 その場合、該当データが無いケースでは、なぜかデータ全体がコピーされてしまいます。 ・これを防止するために、 Application.WorksheetFunction.Subtotal(103, ws.Columns(1)) > 1 のようなヒットしたデータ行を算出して、場合分けする必要があるわけです。 ・しかし、見出しを含めてコピーする場合は、 上記のような場合分けは不要で、該当データが無い場合は、見出しだけがコピーされるのでした。
少しミスリーディングなコメントでした。失礼しました。
(xyz) 2024/04/14(日) 13:09:42
xyz様のコードをそのまま使うと
Rng.Copy newWb.Sheets(wsIndex).[A1] の部分で2巡目にシートが無いためにエラーがでます。
Rng.Copy After:=newWb.Sheets(wsIndex).[A1] にすると、.[A1]を付けているからか?エラーで稼働できません。
更に、Rngをやめて、下記で試すと、結果は貼りつくが、今度はシート名が元のシート名にならず、Sheet1, 2, 3・・・のようになってしまいます。
ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp
Set newWs = newWb.Sheets.Add(After:=newWb.Sheets(newWb.Sheets.Count))
ws.UsedRange.Copy newWs.Range("A1")
xyz様のコードをそのままあてはめたもの
Sub Test中()
    Dim 分割元 As Workbook
    Dim folderPath As String
    Dim i As Long
    Dim ws As Worksheet
    Dim MyDic As Object
    Dim tmp As Variant
    Dim newWb As Workbook
    Dim wsIndex As Long
    Dim Rng As Range
    Set MyDic = CreateObject("Scripting.Dictionary")
    ' 分割元のファイルを読み取り専用で開く
    Set 分割元 = Workbooks.Open(Filename:=ThisWorkbook.Sheets("設定").Range("A2").Value, ReadOnly:=True)
    ' フォルダパスを取得
    folderPath = ThisWorkbook.Sheets("設定").Range("A1").Value
    Application.ScreenUpdating = False
    ' 名前の一覧を取得
    For Each ws In 分割元.Sheets
        If ws.name <> "まとめ" Then
            For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                If Not MyDic.Exists(ws.Cells(i, 1).Value) Then MyDic.Add ws.Cells(i, "A").Value, ""
            Next i
        End If
    Next ws
    ' 各名前ごとにファイルを作成
    For Each tmp In MyDic.keys
        Set newWb = Workbooks.Add
        wsIndex = 1
        For Each ws In 分割元.Sheets
            If ws.name <> "まとめ" Then
                ws.AutoFilterMode = False
                ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp
                Set Rng = ws.[A1].CurrentRegion
                Rng.Copy newWb.Sheets(wsIndex).[A1]
                wsIndex = wsIndex + 1
            End If
        Next ws
        ' 新しいブックの保存 (既にファイル存在の場合は上書きするように警告無視)
          Application.DisplayAlerts = False
          newWb.SaveAs folderPath & "\" & tmp & "_まとめ.xlsx"
          newWb.Close SaveChanges:=True
          Application.DisplayAlerts = True
    Next tmp
    ' 分割元のファイルを閉じる
    分割元.Close SaveChanges:=False
    Application.ScreenUpdating = True
End Sub
(ぽぽ) 2024/04/14(日) 16:05:48
   Rem ********************************************************************************
    For Each ws In 分割元.Sheets
        If ws.Name <> "まとめ" Then
            If wsIndex > 1 Then newWb.Worksheets.Add after:=newWb.Worksheets(wsIndex - 1)
            ws.AutoFilterMode = False
            ws.Range("A1").AutoFilter Field:=1, Criteria1:=tmp
            Set Rng = ws.[A1].CurrentRegion
            Rng.Copy newWb.Sheets(wsIndex).[A1]
            newWb.Sheets(wsIndex).Name = ws.Name
            wsIndex = wsIndex + 1
        End If
    Next ws
    Rem *******************************************************************************
うんなかんじでは。。。どうなるでせう。。。何シートあるのでせうね?^^;
処理速度がすこぉし気になります^^;
m(__)m
(隠居Z) 2024/04/14(日) 16:46:39
色々とありがとうございます。
みなさまからのF/B、大変参考になります。フローが大切なこと、よく分かりました。。
(ぽぽ) 2024/04/14(日) 17:08:55
(隠居Z) 2024/04/14(日) 17:59:03
    Sub ちょっと別案()
        Dim i As Long, c As Long
        Dim ws As Worksheet
        Dim MyDic As Object
        Dim 分割元 As Workbook
        Dim シート名群() As String
        Dim tmp As Variant
        Set 分割元 = Workbooks.Open(Filename:=ThisWorkbook.Sheets("設定").Range("A2").Value, ReadOnly:=True)
        Set MyDic = CreateObject("Scripting.Dictionary")
        For Each ws In 分割元.Sheets
            If ws.Name <> "まとめ" Then
                ws.AutoFilterMode = False
                ws.Range("A1").AutoFilter
                ReDim Preserve シート名群(c) As String
                シート名群(c) = ws.Name
                c = c + 1
                For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                    If Not MyDic.Exists(ws.Cells(i, 1).Value) Then MyDic.Add ws.Cells(i, "A").Value, ""
                Next i
            End If
        Next ws
        Application.DisplayAlerts = False
        For Each tmp In MyDic.keys
            '▼まとめシート以外を新規ブックにコピーする
            分割元.Worksheets(シート名群).Copy
            With Workbooks(Workbooks.Count)
                For Each ws In .Worksheets
                    '▼対象"以外"を抽出して削除する
                    ws.AutoFilter.Range.AutoFilter Field:=1, Criteria1:="<>" & tmp
                    ws.AutoFilter.Range.Offset(1).Delete
                    On Error Resume Next
                    ws.ShowAllData
                    On Error GoTo 0
                Next ws
                .SaveAs Filename:=ThisWorkbook.Sheets("設定").Range("A1").Value & "\" & tmp
                .Close False
            End With
        Next tmp
        Application.DisplayAlerts = True
    End Sub
(もこな2) 2024/04/14(日) 18:45:11
(隠居Z) 2024/04/14(日) 19:41:38
みなさま、色々教えていただき、ありがとうございました。
(ぽぽ) 2024/04/14(日) 20:25:58
質問者さんのコードの延長線にあるものを参考までに示します。 学習の参考になればよいと思います。
 Sub test()
     Dim folderPath As String
     Dim fullPath   As String
     Dim 分割元     As Workbook
     Dim sheetsCount As Long
     Dim dic   As Object
     Dim i     As Long
     Dim k     As Long
     Dim ws    As Worksheet
     Dim key   As Variant
     Dim newWb As Workbook
     Dim newWs As Worksheet
     Dim rng   As Range
     Application.ScreenUpdating = False
     '分割元Bookを開く
     folderPath = ThisWorkbook.Sheets("設定").Range("A1").Value
     fullPath = ThisWorkbook.Sheets("設定").Range("A2").Value
     Set 分割元 = Workbooks.Open(Filename:=fullPath, ReadOnly:=True)
     sheetsCount = 分割元.Worksheets.Count
     ' 名前の一覧を取得
     Set dic = CreateObject("Scripting.Dictionary")
     For Each ws In 分割元.Sheets
         If ws.Name <> "まとめ" Then
             For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                 dic(ws.Cells(i, "A").Value) = Empty
             Next
         End If
     Next ws
     ' 各名前ごとに転記先ブックを作成して転記
     For Each key In dic.keys
         k = 0
         Set newWb = makeNewWorkbook(sheetsCount - 1)  '転記先ブックを作成
         For Each ws In 分割元.Sheets
             If ws.Name <> "まとめ" Then
                 k = k + 1
                 Set newWs = newWb.Worksheets(k)
                 newWs.Name = ws.Name
                 'A列がkeyであるデータのみ抽出してコピーペイスト
                 ws.Range("A1").AutoFilter Field:=1, Criteria1:=key
                 ws.[A1].CurrentRegion.Copy newWs.[A1]
                 ws.AutoFilterMode = False
             End If
         Next ws
         Application.DisplayAlerts = False
         newWb.SaveAs folderPath & "\" & key & "_まとめ.xlsx"
         newWb.Close SaveChanges:=False
         Application.DisplayAlerts = True
     Next key
     ' 分割元のファイルを閉じる
     分割元.Close SaveChanges:=False
     Application.ScreenUpdating = True
 End Sub
 Function makeNewWorkbook(シート数 As Long) As Workbook
     Dim buf As Long
     buf = Application.SheetsInNewWorkbook  '自動で作成するシート数の設定を記憶
     Application.SheetsInNewWorkbook = シート数
     Set makeNewWorkbook = Workbooks.Add
     Application.SheetsInNewWorkbook = buf  '’自動で作成するシート数の設定を復旧
 End Function
(注記) 分割元ブックのシート名は、Workbooks.Addで作成される通常のSheet1,Sheet2・・・とバッティングしないことが条件です。 (例えば、分割元ブックの最初のシート名が Sheet2 だったりすると、 newWs.Name = ws.Name を実行した時点でシート名がバッティングしてエラーになります。) (xyz) 2024/04/14(日) 23:34:32
(隠居Z) 2024/04/15(月) 09:45:34
(トピ主が提示したコードの延長線なのであえてということなのかもしれませんが)
 Sub test_別案()
     Dim 分割元 As Workbook
     Dim dic As Object
     Dim i As Long, k As Long
     Dim ws As Worksheet
     Dim key   As Variant
     Dim newWb As Workbook
     '分割元Bookを開く
     Set 分割元 = Workbooks.Open(Filename:=ThisWorkbook.Sheets("設定").Range("A2").Value, ReadOnly:=True)
     ' 名前の一覧を取得
     Set dic = CreateObject("Scripting.Dictionary")
     For Each ws In 分割元.Sheets
         If ws.Name <> "まとめ" Then
             ws.AutoFilterMode = False
             ws.Range("A1").AutoFilter
             For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
                 dic(ws.Cells(i, "A").Value) = Empty
             Next
         End If
     Next ws
     ' 各名前ごとに処理
     For Each key In dic.keys
        Set newWb = Workbooks.Add(xlWBATWorksheet) '引数を指定するとそのタイプのシート1枚だけのブックができる
        newWb.Worksheets(1).Name = "後で削除するシート" '名前被り対策(持ってくるシートがSheet1じゃなきゃ不用)
         'まとめシート以外を順番に処理(抽出して、全体をコピーして、シートを追加してから貼付する)
         For Each ws In 分割元.Sheets
             If ws.Name <> "まとめ" Then
                 ws.Range("A1").AutoFilter Field:=1, Criteria1:=key
                 ws.AutoFilter.Range.Copy Worksheets.Add(after:=newWb.Worksheets(newWb.Worksheets.Count)).Range("A1")
                 newWb.Worksheets(newWb.Worksheets.Count).Name = ws.Name
             End If
         Next ws
         Application.DisplayAlerts = False
         newWb.Worksheets(1).Delete '最初のシートはここで削除
         newWb.SaveAs ThisWorkbook.Sheets("設定").Range("A1").Value & "\" & key & "_まとめ.xlsx"
         newWb.Close SaveChanges:=False
         Application.DisplayAlerts = True
     Next key
     ' 分割元のファイルを閉じる
     分割元.Close SaveChanges:=False
     Application.ScreenUpdating = True
 End Sub
あと、セル範囲をコピーするアプローチだと、行高や列幅にこだわりがある場合ちょっと面倒かもしれません。
(もこな2) 2024/04/16(火) 07:08:13
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
 Modified by kazu.