ラベル マクロ の投稿を表示しています。 すべての投稿を表示
ラベル マクロ の投稿を表示しています。 すべての投稿を表示

2/19/2023

Excel。VBA。条件に合致したデータが含まれる行全体を塗りつぶしたい【whole line】

Excel。VBA。条件に合致したデータが含まれる行全体を塗りつぶしたい

<Excel VBA:Interior>

条件に合致するデータがある行全体を塗りつぶすには、条件付き書式の新しいルールで、数式をつかった条件を設定することで、対応することができます。

 

ただ、データを読みこんだ後に、毎回同じ条件付き書式を設定するのも面倒です。

そこで、Excel VBAでプログラム文をつくると、作業効率が改善できるわけです。


Excel VBAで条件付き書式を設定しなくても、範囲選択を工夫することで、それほど、難しくないプログラム文で対応することができます。


今回は、E列の合計が210以上だったら、行全体を塗りつぶすことにします。


Sub 行塗りつぶし210以上()

    Dim i As Integer

    Dim lastrow As Long   

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row


    For i = 2 To lastrow

        If Cells(i, "e") >= 210 Then

            With Range(Cells(i, "a"), Cells(i, "e")).Interior

                .ThemeColor = xlThemeColorAccent2

                .TintAndShade = 0.8

            End With

        End If

    Next

End Sub


このプログラム文を実行すれば、手早くE列の合計が210以上のデータの場合、その行全体を塗りつぶすことができます。


プログラム文を説明していきます。


最初は、変数宣言です。

Dim i As Integer

Dim lastrow As Long


For To Next文で繰り返し処理を行うので、繰り返す回数をlastrowに代入させます。

lastrow = Cells(Rows.Count, "a").End(xlUp).Row


繰り返し処理の中でIf文をつかって、E列のデータが210以上か判定します。

For i = 2 To lastrow

    If Cells(i, "e") >= 210 Then

        With Range(Cells(i, "a"), Cells(i, "e")).Interior

            .ThemeColor = xlThemeColorAccent2

            .TintAndShade = 0.8

        End With

    End If

Next


210以上の時には、塗りつぶしの処理をします。

With文をつかっているのは、同じことを繰り返し入力するのは面倒なので、省略することもありますが、可読性を高めるために使用しています。


「Range(Cells(i, "a"), Cells(i, "e"))」という範囲選択が行全体に対応しています。

この範囲選択のやり方を知らないと、行と列で繰り返し処理をすることになります。


「.Interior」が塗りつぶし処理です。

何色にするのかが、

「.ThemeColor = xlThemeColorAccent2

  .TintAndShade = 0.8」

です。


これらのプロパティは、テーマの色のパレットを指し示す指示をしています。


詳しくは、このBLOG内に記載しておりますので、ご参考まで。

「Excel。テーマの色の設定値ってこんな風になっているんですね。」

https://infoyandssblog.blogspot.com/search?q=vba+%E3%83%86%E3%83%BC%E3%83%9E%E3%81%AE%E8%89%B2

2018年1月2日 公開

11/07/2022

Excel。えっ!マクロが実行できない!セキュリティリスクが表示されました。【SecurityRisk】

Excel。えっ!マクロが実行できない!セキュリティリスクが表示されました。

<Excel VBA:マクロ:セキュリティリスク対応>

日頃、マクロを実行していたファイル。

開くと、「セキュリティリスク」が表示されてしまい、マクロをつかうことができなくなりました。


どのように対応したら、マクロを使うことができるのでしょうか。


一度、Excelを閉じて、そのファイルの上で右クリックをします。

そして、プロパティをクリックします。


ファイルのプロパティダイアログボックスが表示されます。

セキュリティの「許可をする」にチェックマークをオンとするだけで、マクロを使えるようになります。


あとは、OKボタンをクリックして、再度ファイルを開きます。


「セキュリティの警告」が表示されるので、「コンテンツの有効化」をクリックします。


通常通り、マクロを使うことができます。

1/06/2022

Excel。VBA。大量のシート名を指定したシートのコピーを手早く行いたい【Copy of sheet】

Excel。VBA。大量のシート名を指定したシートのコピーを手早く行いたい

<Excel VBA>

単純な作業ほど、繰り返して処理をするとなると、面倒に感じます。

そこで、Excel VBAでマクロをつくって実行させる方が作業効率としても改善できるし、面倒な作業から解放されるわけですね。


そこで、今回は、次のようなケースの対応方法をExcel VBAで対応していきます。


シート店舗一覧には、店舗一覧が用意されています。


 

シート原版は、テンプレシートのシートです。


やりたい処理は、シート原版をコピーします。

コピーしたシートは、A1とシート名をシート店舗一覧から設定します。

作成するシート数は、シート店舗一覧の横浜店から強羅店までです。


作業としては、簡単ですが、店舗数分繰り返すとなると、面倒ですね。


そこで、Excel VBAでプログラミングをつくって対応したいというわけです。


Sub シート作成()

    Dim i As Long

    Dim sheet_name As String

    Dim lastrow As Long


    sheet_name = ""

    lastrow = Worksheets("店舗一覧").Cells(Rows.Count, "a").End(xlUp).Row


    For i = 2 To lastrow

        sheet_name = Worksheets("店舗一覧").Cells(i, "a")

        ThisWorkbook.Sheets("原版").Copy after:=Sheets(Sheets.Count)

        ActiveSheet.Name = sheet_name

        Range("a1").Value = sheet_name

    Next

End Sub


実行してみましょう。


店舗一覧の店舗シートを作成することができました。


では、プログラミング文を確認していきましょう。


お馴染み変数宣言ですね。

Dim i As Long 

Dim sheet_name

Dim lastrow As Long


sheet_nameは、店舗名を代入して使用する変数です。


lastrow = Worksheets("店舗一覧").Cells(Rows.Count, "a").End(xlUp).Row

繰り返し処理のために、シート店舗一覧の店舗数を代入しているのが、lastrowです。


For i = 2 To lastrow

    sheet_name = Worksheets("店舗一覧").Cells(i, "a")

    ThisWorkbook.Sheets("原版").Copy after:=Sheets(Sheets.Count)

    ActiveSheet.Name = sheet_name

    Range("a1").Value = sheet_name

Next


For i = 2 To lastrow~Next

For To Next文をつかって、繰り返し処理をします。

2から開始しているのは、見出し行を除いたデータがA2にあるからです。


sheet_name = Worksheets("店舗一覧").Cells(i, "a")

シート店舗一覧のA列の店舗名をsheet nameに代入します。


ThisWorkbook.Sheets("原版").Copy after:=Sheets(Sheets.Count)

シート原版をシートの最後尾にコピーします。

「after:」とすることで、そのシートの右側にコピーすることができます。

「Sheets.Count」はシート数を算出します。


例えば、シート数が3枚の場合だと、

「after:=Sheets(Sheets.Count)」は、Sheet(3)の右側にシートをコピーするという意味になります。


ActiveSheet.Name = sheet_name

コピーしたシート名を、代入した名前に置き換えます。


Range("a1").Value = sheet_name

A1に店舗名を設定します。


このように、比較的シンプルなプログラム文で、作業効率を改善することができます。

日頃行っている作業で、面倒なものなどがあれば、Excel VBAをつかって、マクロをつくってみるというのもいいかもしれませんね。

11/22/2021

Excel。VBA。名簿からフリガナの行単位で抽出したい【extract】

Excel。VBA。名簿からフリガナの行単位で抽出したい

<Excel VBA>

簡単そうに思う作業程、結構面倒ということがあります。

例えば、次のような表があります。


やりたいことは、B列の氏名がカ行の人をD列に抽出したいわけです。

このカ行という行単位のデータというのが、厄介なんですね。


作業を具体的に考えるとしたら、フリガナを表示させて、その左一文字がカ~コなのか判定させて、合致したデータを抽出先に、コピーするという感じでしょうか。


かなり面倒です。


しかし、Excel VBAには、フリガナを行単位で比較的簡単に抽出する方法があるのです。


その方法は、ワイルドカードを使う方法です。


ワイルドカードをつかったら、抽出できるのでしょうか?


そもそも、最初の一文字目が異なりますので、例えば「カ*」としても、「キ~」のデータは合致しませんので、抽出対象にはなりません。


どうしてもLEFT関数をつかい、フリガナを表示するPHONETIC関数をつかうことになるわけです。


ところが、Excel VBAのワイルドカードは、Accessのように、フリガナの行選択で抽出することができるのです。


では、プログラム文を作っていきます。

Sub カ行抽出()

    Dim i As Long

    Dim lastrow As Long

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    

    For i = 2 To lastrow

        If Cells(i, "b").Phonetic.Text Like "[カ-コ]*" Then

            Cells(i, "b").Copy Cells(lastrow, "d").End(xlUp).Offset(1, 0)

        End If

    Next i

End Sub


これを実行すると、カ行に該当するデータを抽出することができました。


プログラム文を確認してみましょう。


お馴染み、変数宣言です。

Dim i As Long

Dim lastrow As Long

    

lastrow = Cells(Rows.Count, "a").End(xlUp).Row


Lastrowは、データの最終行番号です。

この数値をつかって、このあとのFor文の繰り返し数で使用します。


For i = 2 To lastrow ~ Next i

For文です。見出しを除くので、2から、最終レコード行数までを繰り返し処理をします。


If Cells(i, "b").Phonetic.Text Like "[カ-コ]*" Then End If

ここが、今回のカ行を抽出するところです。


「Phonetic.Text」で、該当するセルのフリガナ情報をGetします。

そして、Like "[カ-コ]*"。


Like演算子をつかった、ワイルドカードで行を抽出できます。


「[カ-コ]*」とすると、「カ*」「キ*」…「コ*」のように、カ~コで始まるデータという意味になります。


Excel VBAでは、可能なのですが、通常のExcelのワイルドカードでは、"[カ-コ]*"という設定を行うことができません。


Excelではできないけど、Excel VBAならできるということが、ありますので、Excel VBAの知識も増やしていくと、効率の良い解決方法が、見つかるかもしれません。


If文の中の処理は、

Cells(i, "b").Copy Cells(lastrow, "d").End(xlUp).Offset(1, 0)

該当するデータがあったら、D列にコピーします。

なお、.End(xlUp).Offset(1, 0)で、最終データのさらに下のセルと定義することができますので、データを上書きすることはありません。

11/07/2021

Excel。VBA。抽出したデータの見出し行を除いて別シートにコピーしたい【Data only】

Excel。VBA。抽出したデータの見出し行を除いて別シートにコピーしたい

<VBA>

データを読み込んだ後に、抽出だけを行って作業終了というよりも、その抽出したデータを別シートにコピーするなど、抽出後の作業が伴うことになるかと思います。


抽出したデータをコピーすること自体は、厄介ではないのですが、「見出し行」を除いてデータのみを別シートにコピーしたい場合は、どのようにしたらいいのでしょうか。


コピーする範囲をOffsetなどつかって、見出し行を除いた範囲を設定する方法もあるかもしれませんが、CurrentRegionプロパティをつかうことで、見出し行も含めてコピーすることができます。

そのため、コピーした後に見出し行を削除するほうが、簡単にデータのみにすることができます。


次のデータを使います。


店舗番号が3の品川店のデータのみを別シートにコピーするプログラム文を作っていきます。


Sub 見出し行除き()

    Range("a1").AutoFilter field:=2, Criteria1:=3

    

    If WorksheetFunction.Subtotal(3, Range("b:b")) > 1 Then

        Worksheets.Add after:=Sheets(Sheets.Count)

        Worksheets("data").Range("a1").CurrentRegion.Copy Range("a1")

        Range("a1").EntireRow.Delete

        Worksheets(Sheets.Count).Name = "copy"

        Worksheets("data").Range("a1").AutoFilter

    End If

End Sub


とてもシンプルなプログラム文で、見出し行を除いてコピーすることができます。

それでは、実行してみましょう。


 

このように、別シートに見出し行を除いたデータのみをコピーすることができました。


プログラム文を見ていきましょう。

Range("a1").AutoFilter field:=2, Criteria1:=3

この行は、オートフィルターをつかって抽出作業を行っています。


AutoFilter で、オートフィルターを設定します。

field:=2 は、左から2列目のフィールドを指しますので、「店舗番号」の列が該当します。


Criteria1:=3 の「Criteria」は条件。

つまり抽出条件のことなので、「3」の品川店を抽出する設定のオートフィルターを設定することができます。


If WorksheetFunction.Subtotal(3, Range("b:b")) > 1 Then ~End If

このIf文は、何をやっているのかというと、該当するデータがあれば、それ以降を実行して、抽出データがなければ、実行しないためのプログラム文です。


WorksheetFunction.Subtotal は、ワークシート関数のSubtotal関数をつかって、データのあるなしを確認しています。


Worksheets.Add after:=Sheets(Sheets.Count)

データがあることが判明したので、コピーを行うわけですが、コピー先のシートがないので、Worksheets.Add でシートを追加します。


なお、after:=Sheets(Sheets.Count)で、最終シートの後ろにシートを追加させます。Sheets.Count でブック内のシートの枚数を確認することができます。


Worksheets("data").Range("a1").CurrentRegion.Copy Range("a1")

追加したシートのA1に、抽出したデータをコピーします。



Range("a1").EntireRow.Delete

コピーしたデータの、1行目を削除します。これで、見出し行を除いたデータにすることができました。


Worksheets(Sheets.Count).Name = "copy"

追加したシート名が設定されていませんので、「copy」というシート名で設定します。


Worksheets("data").Range("a1").AutoFilter

コピー作業が終了したので、抽出するためのオートフィルターを解除します。


これ以外にも、様々な方法で作ることができますので、色々考えてみるのもいいかもしれませんね。

10/14/2021

Excel。VBA。印刷設定を毎回行うのは面倒なので手早く設定したい【Print settings】

Excel。VBA。印刷設定を毎回行うのは面倒なので手早く設定したい

<Excel VBA>

データを読み込んだり、ファイルを開いたりしたあとに、印刷する場合、様々な設定をしないといけません。


次のデータを用意しました。


印刷設定として、よく行う作業としては、


・横1ページに収めるようにする

・フッターに ページ数/総ページ数 を表示する

・2ページ目以降にも見出し行が印刷されるようにする


といったところでしょうか。

これらの処理を、毎回設定して印刷するというのは、面倒ですし、意外と設定に時間がかかります。

できることなら、手早く印刷したいところです。



そこで、Excel VBAでマクロをつくって、実行すると、手早く設定した状態で印刷することが出来るようになります。


本来ならば、2行程度が2ページ目に送られているので、1枚に収めると思いますが、今回は、フッターや見出し行が複数ページに印刷されるようになっているのか、確認したいので、2ページとしています。


プログラム文は、次のとおりです。

Sub 印刷設定()

    With ActiveSheet

        '横1ページに収める

        .PageSetup.Zoom = False

        .PageSetup.FitToPagesWide = 1

        .PageSetup.FitToPagesTall = False


        'フッター中央にページ番号

        .PageSetup.CenterFooter = "&P/&N"

        

        'タイトル行の設定

        .PageSetup.PrintTitleRows = "$1:$1"

        

        'プレビュー画面を表示

        .PrintPreview

    End With

End Sub


とりあえず、実行してみます。


横1ページに収めるようにする

フッターに ページ数/総ページ数 を表示する

2ページ目以降にも見出し行が印刷されるようにする


これらが、きちんと反映されています。


では、プログラム文を確認していきましょう。


With ActiveSheet ~ End With

アクティブのシートを印刷します。プログラム文に「ActiveSheet」と入力するのは、面倒なので、With文をつかって、省略させています。

プレビューを除いて、本来は、「With ActiveSheet.PageSetup」とするといいですね。


'横1ページに収める

.PageSetup.Zoom = False

.PageSetup.FitToPagesWide = 1

.PageSetup.FitToPagesTall = False


横は1ページに収めるようにさせるのが、このブロック。

.PageSetup.Zoom = False は、-拡大・縮小率を指定しないようにさせています。

.PageSetup.FitToPagesWide = 1 は、横方向1ページで印刷するように設定します。

.PageSetup.FitToPagesTall = False は、縦方向はそのまま自動で対応とします。1ページに収める場合は、「1」とします。


'フッター中央にページ番号

.PageSetup.CenterFooter = "&P/&N"

フッターの中央(CenterFooterプロパティ)に、「&P」のページ番号と区分けするための「/」と総ページ数の「&N」とすること、ページ数/総ページ数をフッター中央に表示することができます。


'タイトル行の設定

.PageSetup.PrintTitleRows = "$1:$1"

見出し行の1行目を設定しています。

これは、ページ設定ダイアログボックスのタイトル行を設定する作業そのものです。


 'プレビュー画面を表示

.PrintPreview

最後の、PrintPreviewをつかうことで、印刷プレビューで確認することができます。


たった、数行ですが、このマクロを実行することだけで、印刷設定を瞬時に完了することができます。

9/23/2021

Excel。VBA。表から特定の列(フィールド)だけを手早くコピーしたい【COPY】

Excel。VBA。表から特定の列(フィールド)だけを手早くコピーしたい

<Excel VBA>

データから必要な列(フィールド)だけを手早くコピーするとしたら、どうしたらいいのでしょうか?


例えば、次の表。


A列の「NO」とF列の「販売日」だけをコピーしたいとします。

自力でコピーしてもいいのですが、もっと多くの列だと、面倒です。


単純な作業ほど、繰り返し実行するとなると、効率的に作業できる方がいいですよね。


そこで、Excel VBAでマクロをつくると、とても簡単に、手早くコピーすることができます。


では、Excel VBAのプログラム文をつくってみました。

Sub 特定列コピー()

    Dim hani As Range

    Dim tokutei As Variant

    Dim i As Long

    

    Set hani = Range("a1").CurrentRegion

    

    tokutei = Array(1, 6)

    

    For i = 0 To UBound(tokutei)

        hani.Columns(tokutei(i)).Copy Range("i1").Offset(0, i)

    Next

    Range("i1").CurrentRegion.Columns.AutoFit

End Sub


まずは実行してみましょう。


ご覧のように、I列とJ列に該当するデータをコピーすることができました。


プログラム文を確認していきます。

最初は、お馴染みの「宣言文」

Dim hani As Range

Dim tokutei As Variant

Dim i As Long


haniには、表の範囲を代入します。

tokureiには、配列としてコピーしたい特定の列番号を代入します。


Set hani = Range("a1").CurrentRegion

haniに、A1から連続するデータ(表)の範囲を代入します。


tokutei = Array(1, 6)

範囲の中から、抽出したい列番号をしていします。

今回は、1列目と6列目をコピーしたいので、Array(1, 6)と設定します。


For i = 0 To UBound(tokutei)

    hani.Columns(tokutei(i)).Copy Range("i1").Offset(0, i)

Next


For To Next文でコピーを繰り返します。

iが0なのは、配列は「0」から始まるからです。


UBoundは、指定した配列で使用できる添え字の最大値を算出することができます。

つまり、UBoundをつかうことで、繰り返し回数を決めることができます。


hani.Columns(tokutei(i)).Copy Range("i1").Offset(0, i)

指定した列をコピーして貼り付けます。

Offsetをつかうことで、隣の列に貼り付けることができます。


最後の

Range("i1").CurrentRegion.Columns.AutoFit

この行は、貼り付けた列幅を自動調整させることで、「####」と表示されないようにしています。


少ないプログラム文ですが、簡単な作業でも、手早く処理することができますので、Excel VBAでプログラムをつくってみるのもいいかもしれませんね。

9/05/2021

Excel。VBA。ROUND関数をExcel VBAでつかうと四捨五入の結果が変なんです。【Rounding】

Excel。VBA。ROUND関数をExcel VBAでつかうと四捨五入の結果が変なんです。

<Excel VBA:ROUND>

四捨五入でお馴染みの、「ROUND関数」。

データをインポートした後に、ROUND関数をつかった計算式をつくるのもいいのですが、まとめて処理をしたほうが、楽だろうということで、Excel VBAでROUND関数をつかってプログラム文をつくると、普通のROUND関数と算出結果が異なるわけです。


次の表をつかって説明します。


C列は、A列÷B列で算出した結果です。


C2の数式は、

=A2/B2

としています。算出結果を四捨五入したのが、D列です。


D2の数式は、

=ROUND(C2,0)

第2引数を「0(ゼロ)」で設定したことにより、小数点第一位を四捨五入しています。


D列の算出結果は、なんら問題はないことがわかります。


E~G列は、Excel VBAでプログラム文を作ったものを実行していますが、E列のデータの一部に算出結果におかしな結果があります。

それでは、Excel VBAのプログラム文を見てみましょう。

Sub round関数()

    Dim i As Long

    Dim lastrow As Long

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    

    For i = 2 To lastrow

        Cells(i, "e").Value = Round(Cells(i, "c"), 0)

        Cells(i, "f").Value = WorksheetFunction.Round(Cells(i, "c"), 0)

        Cells(i, "g").Value = Int(Cells(i, "c") + 0.5)

    Next

End Sub


簡単な説明ではありますが、変数宣言をしています。

Dim i As Long

Dim lastrow As Long

    

lastrow = Cells(Rows.Count, "a").End(xlUp).Row

「i」はこのあとのFor To Next文で使用します。

lastrowは、データの最終行の行番号を算出しています。

For To Next文でlastrowまで繰り返し実行するわけです。


For To Next文の中身を確認します。

Cells(i, "e").Value = Round(Cells(i, "c"), 0)

E列に算出しているのが、このプログラム文です。


Round(Cells(i, "c"), 0) と、Round関数を使用しています。

プログラム文自体おかしなところはありませんが、算出結果に、おかしな結果が出ているわけです。


実は、VBAのRound関数は、「四捨五入」するものではなくて、「数値を丸める」処理をする関数なのです。

4以下の場合は切り捨て、5以上の場合は切り上げという処理はしてくれないわけです。


偶数の場合、丸める値が、5だと、切り捨てをする「銀行での丸め処理」と同じで、AccessのRound関数と同じ動きをしています。


だから、通常のExcelと同じようにプログラム文をつくってしまうと、算出結果が、変わってしまうというわけです。


なので、Excel VBAで通常のExcelと同じ算出結果にしたい。

つまり通常のROUND関数と同じ算出結果にするには、次の2つの対応方法があります。


1つ目が、F列のWorksheetFunctionのROUND関数をつかいます。


WorksheetFunctionは、通常のExcel関数を用意しています。

今回のROUNDはWorksheetFunctionに用意されているので使用することができます。


WorksheetFunctionをつかったプログラム文が、

Cells(i, "f").Value = WorksheetFunction.Round(Cells(i, "c"), 0)


基本的に、WorksheetFunctionをつかうことで、問題は解決しますが、次のような方法もあります。


それが、2つ目の、

Cells(i, "g").Value = Int(Cells(i, "c") + 0.5)

これは、元の値に丸める「0.5」を加算した値を、整数化することで、ROUND関数を同じように算出することができます。


ただし、今回は、整数化した、四捨五入だったので、この数式で対応できますが、小数点第二位とか、色々かわると、加算値を考えないといけないので、WorksheetFunctionをつかうのがいいのかなと思います。


ということで、Excel VBAでROUND関数をつかうと、通常のROUND関数と算出結果が異なるので注意が必要です。

8/21/2021

Excel。VBA。並べ替えを4つ以上の列で実行したいけど、どうやったらいいの【SORT】

Excel。VBA。並べ替えを4つ以上の列で実行したいけど、どうやったらいいの

<Excel VBA:SORT>

並べ替えを4つ以上の列で実行したい場合は、条件を優先順位の逆で実行すれば、並べ替えることができます。

ただ、列数が増えると、順番がわからなくなったりするので、処理が面倒といえば面倒です。


次の表を用意しました。


この表を使って、Excel VBA でプログラム文をつくって、「地域→フリガナ→売上高→販売日)」という優先順位で並べ替えを実行します。

また、売上高は降順で、それ以外は、昇順とします。


Sub 並べ替え4列()

    Dim hani As Range

    Dim sortorder As Variant

    Dim i As Long

    Dim lastrow As Long

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    

    Range("d2", Cells(lastrow, "d")).SetPhonetic

    

    Set hani = Range("a1").CurrentRegion

    sortorder = Array("販売日", "売上高", "フリガナ", "地域")

    

    For i = 2 To lastrow

        Range("f" & i) = Range("d" & i).Phonetic.Text

    Next

    

    i = 0

    

    With hani

        For i = 0 To UBound(sortorder)

            If i = 1 Then

                .Sort key1:=sortorder(i), order1:=xlDescending, Header:=xlGuess

            Else

                .Sort key1:=sortorder(i), order1:=xlAscending, Header:=xlGuess

            End If

        Next

    End With

End Sub


では、実行してみましょう。


希望通りに4列での並べ替えを実行することができました。


では、プログラム文を見ていきます。

変数宣言です。

Dim hani As Range

Dim sortorder As Variant

Dim i As Long

Dim lastrow As Long

    

データの最後の行番号を取得します。繰り返しで使用します。

lastrow = Cells(Rows.Count, "a").End(xlUp).Row


店舗名で並べ替えを実行したいのですが、データを別のアプリケーションからもってきたとか、フリガナデータがないかもしれません。

フリガナデータを設定します。

Range("d2", Cells(lastrow, "d")).SetPhonetic


SetPhoneticメソッドでフリガナデータを設定できます。


並べ替えを実行する範囲を、変数haniに代入します。

Set hani = Range("a1").CurrentRegion


sortorderに、優先順位と逆の順番で、配列に設定します。

sortorder = Array("販売日", "売上高", "フリガナ", "地域")

    

Arrayは配列という意味です。


For i = 2 To lastrow

    Range("f" & i) = Range("d" & i).Phonetic.Text

Next

フリガナの列にフリガナが表示されていないので、表示させます。

店舗名にフリガナデータがありますので、それをF列に表示させるというのは、PHONETIC関数と同じ考え方です。


ここからが、並べ替えの本体です。

i = 0

念のため、変数iを0で初期化します。

なぜ、1ではないのかというと、次に配列を使うからです。

配列は0(ゼロ)から始まるからです。

    

With hani ~ End With

With文で、haniという変数を繰り返えして入力する手間を省いています。


For文です。配列の0番目から処理を始めます。

UBound(sortorder)は、sortorderの最大値を算出することができます。

UBoundは、配列の時に知っていると便利です。


すべて、昇順や降順ならば、If~Else~EndIf分は不要ですが、今回は、売上高を降順にしたいので、If文をつかって、条件分岐をしています。


sortorder = Array("販売日", "売上高", "フリガナ", "地域")なので、

販売日がsortorder(0)

売上高がsortorder (1)

フリガナがsortorder (2)

地域がsortorder (3)

と配列に代入されています。


iが1。

つまり、売上高フィールドならば、

.Sort key1:=sortorder(i), order1:=xlDescending, Header:=xlGuess


それ以外は、

.Sort key1:=sortorder(i), order1:=xlAscending, Header:=xlGuess


という処理をしています。


大きなデータになると、4列以上で並べ替えを行うことがありますので、処理が煩雑でわかりにくいと感じたら、Excel VBAでプログラムをつくってみてもいいかもしれませんね。

8/06/2021

Excel。VBA。結合セルのあるデータを結合セルで並べ替えるにはどうしたいい。【Merged cell】

Excel。VBA。結合セルのあるデータを結合セルで並べ替えるにはどうしたいい。

<Excel VBA>

Excelを、帳票のように使うのと、データベースのルールに基づいて使うのでは、Excelの機能を有効に使える使えないということがあります。

特にやりがちなのが、「セルの結合」。


例えば次の表で説明していきます。


A列は、A2:A5までセルの結合をされています。

このように、A列は、複数のデータをまとめてある状態です。


このようなセルの結合の帳票はよく見かけます。

見た目はいいのですが、店舗名を五十音順で並べ替えを行いたいとすると、メッセージが表示されて実行することができません。


店舗名で並べ替えをおこなうには、セルの結合を解除します。


解除すると空白セルになっているので、並べ替えを実行できないので、該当する店舗名を入力あるいは、コピーする作業をおこない、その後、店舗名を五十音順で並べ替えをしたあとに、再度、セルの結合を行うという、ハッキリいって、「やりたくない」作業ですね。


ただただ面倒。


そこで、Excel VBAでプログラムを作って処理をさせるのがいいかと思います。


そのExcel VBAのプログラム文です。

Sub セル結合解除()

    Dim i As Long

    Dim j As Long

    Dim start As Long

    Dim lastrow As Long

    

    Application.ScreenUpdating = False  '画面がひんぱんに切り替わるのを止める

    Application.DisplayAlerts = False   '確認メッセージを非表示にする

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    

    For i = 2 To lastrow

        Cells(i, "a").UnMerge

    Next


    For i = 2 To lastrow

        If Cells(i + 1, "a") = "" Then

            If lastrow <= i + 1 Then

                Exit For

            Else

                Cells(i + 1, "a").Value = Cells(i, "a")

                Cells(i + 1, "a").SetPhonetic

            End If

        End If

    Next

    

    With Range("a1").CurrentRegion

        .Sort key1:="店舗名", order1:=xlAscending, Header:=xlYes

    End With

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    j = 0

    start = 2

    

    For i = 2 To lastrow

        If Cells(i + 1, "a") = Cells(i, "a") Then

            j = j + 1

        Else

            Range(Cells(start, "a"), Cells(start + j, "a")).Merge

            start = i + 1

            j = 0

        End If

    Next

    

    Application.ScreenUpdating = True

    Application.DisplayAlerts = True

    

End Sub


ちょっと長いですが、実行してみましょう。


このように、店舗名はセルの結合をしてある状態で、五十音順で並べ替えが実行されています。


結合するセルの数が異なっていても、対応しています。


では、プログラム文を説明していきます。


お馴染みの変数宣言からです。使うところで、何に使用しているのか説明します。

Dim i As Long

Dim j As Long

Dim start As Long

Dim lastrow As Long


次の2行は、入れておくと便利な2行です。

Application.ScreenUpdating = False  '画面がひんぱんに切り替わるのを止める

Application.DisplayAlerts = False   '確認メッセージを非表示にする


Application.ScreenUpdating = False は、画面がひんぱんに切り替わるのを止めるとめもしてありますが、並べ替えを実行したり、空白のセルにデータをコピーしたりするので、この行を入れておきます。

Falseにすることで、作業が画面表示されないので、処理が早くなります。


Application.DisplayAlerts = False は、確認メッセージを非表示にする とメモをいれていますが、セルの結合を解除したり、セルの結合を再度行う時に、いちいちメッセージが表示され、ボタンを押さないと実行しないというのは、面倒なので、Falseを設定することで、メッセージを非表示にすることができます。


データの最終行番号を取得するのが次の行。

lastrow = Cells(Rows.Count, "a").End(xlUp).Row


この行番号まで繰り返し処理をさせます。


このFor To Next文は、セルの結合を解除させています。

For i = 2 To lastrow

    Cells(i, "a").UnMerge

Next

「UnMergeメソッド」でセルの結合を解除することができます。


2つ目のFor To Next文は、セルの結合を解除したあとの処理です。

For i = 2 To lastrow

    If Cells(i + 1, "a") = "" Then

        If lastrow <= i + 1 Then

            Exit For

        Else

           Cells(i + 1, "a").Value = Cells(i, "a")

            Cells(i + 1, "a").SetPhonetic

        End If

    End If

Next


やっている処理は、なんなのかというと、セルの結合を解除したので、セルの結合されていた一番上のセルにしかデータ(店舗名)がありません。


空白のセルに店舗名を入力(コピー)しています。

このあとに、五十音順に並べ替えをするのですが、入力文字情報が欠落しているので、フリガナ情報もいれています。


また、最終行ならば、For文を抜ける作業をさせています。


ちょっと細かく見ていきます。

次のセルの値が空白だったら、次の処理をします。


さらに、If文で条件分岐させています。

If Cells(i + 1, "a") = "" Then 

この文は、次のセルが空白かどうか、確認するための文です。


If lastrow <= i + 1 Then

    Exit For

Else

最終行ならば、「Exit For」。For文を抜けるという命令をしています。


Cells(i + 1, "a").Value = Cells(i, "a")

この文は、空白セルに、店舗名を入力する作業を行っています。


Cells(i + 1, "a").SetPhonetic

入力した店舗名には、フリガナ情報がないので、「SetPhoneticメソッド」で入力文字情報を追加しています。

この情報をいれないと、綺麗に五十音順で並べ替えを行うことができません。


With文は、店舗名を五十音順で並べ替えを行っています。

With Range("a1").CurrentRegion

    .Sort key1:="店舗名", order1:=xlAscending, Header:=xlYes

End With


A1から連続(CurrentRegion)している範囲を、店舗名を基準(key1)で、五十音順(昇順: xlAscending)とします。また、一行目を見出し行(Header:=xlYes)とするので、それを除き、並べ替える(Sort)ということをしています。


並べ替えが終わったので、再度、セルの結合を行います。


For i = 2 To lastrow

    If Cells(i + 1, "a") = Cells(i, "a") Then

        j = j + 1

    Else

        Range(Cells(start, "a"), Cells(start + j, "a")).Merge

        start = i + 1

        j = 0

    End If

Next


If文の真の処理は、次のセルのデータも同じだったら、結合するセル数をカウントしています。


偽の処理は、セルの接合の処理をしています。


startという変数は、セル結合の先頭の行番号をいれています。

その行番号に同じ店舗名の件数が代入している、変数Jを足したものを範囲として、セルの結合をしています。


このような流れで処理をすることで、セルの結合してあるデータの並べ替えを行ってみました。


本来は、データベースの考え方で、セルの結合は行わず、レコードとしてすべてのセルにデータを入力してほしいところですね。

7/22/2021

Excel。VBA。新宿と品川だったらなどOR条件の時は、SELECT CASE文がオススメです【SELECT】

Excel。VBA。新宿と品川だったらなどOR条件の時は、SELECT CASE文がオススメです

<Excel VBA>

データから、条件に合わせて処理をしたい時には、データを読み込んだ後に数式を作ったり、自分で入力したりするわけですが、できることならば、データを読み込んだ後に、合わせて処理をするほうが、作業的には楽で、効率的に次の作業をすることができます。

例えば、次の表。


C列の地域フィールドにデータがないので、店舗名が「新宿」「品川」だったら、東京。「横浜」「川崎」だったら、神奈川と入力させる処理をしたいとします。


いろいろな方法で入力する作業をするならば、データを読み込んだ時に、地域名が入力されていれば、作業効率がいいわけですね。

目視で自力で入力するのは、時間がかかってしまいますし、数式をつくるのも、意外と面倒です。


今回のように、いわゆる「OR条件」の場合、SELECT CASE文をつかって対応するといいように思えます。


このようにプログラム文をつくってみました。

Sub or条件()

    Dim i As Long

    Dim lastrow As Long

    

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row


    For i = 2 To lastrow

        Select Case Cells(i, "D").Value

            Case "新宿", "品川"

                Cells(i, "C").Value = "東京"

            Case "横浜", "川崎"

                Cells(i, "C").Value = "神奈川"

            Case Else

                Cells(i, "C").Value = ""

        End Select

    Next

End Sub


説明は後回しにするとして、実行してみましょう。


C列の地域フィールドに、東京や神奈川といった地域名が入力されているのが確認できました。


では、プログラム文を確認しておきましょう。

最初は、変数の宣言文です。

Dim i As Long

Dim lastrow As Long

    

変数iは、For to Next文で使用します。


lastrowは、データの最終行の行番号を取得するための変数です。

lastrow = Cells(Rows.Count, "a").End(xlUp).Row

の行で、データの最終行を取得することができます。


この最終行の行数を取得することで、何度繰り返し処理をすればいいのか設定することができます。


For i = 2 To lastrow ~ Next

For To Next文で繰り返し処理を行います。


Select Case Cells(i, "D").Value

    Case "新宿", "品川"

        Cells(i, "C").Value = "東京"

    Case "横浜", "川崎"

        Cells(i, "C").Value = "神奈川"

    Case Else

        Cells(i, "C").Value = ""

End Select

処理の本体である、Select Case文です。


Case "新宿", "品川"

    Cells(i, "C").Value = "東京"

「新宿」と「品川」だったら、C列に「東京」という文字を入力するという意味ですね。

これを必要なパーツ数つくります。


最後に、該当しない場合も作っておく必要がありますので、

Case Else

    Cells(i, "C").Value = ""

とすることで、Select Case文が完了します。


とてもわかりやすく、比較的簡単なプログラム文でつくることができますので、Select Case文も知っておくといいかもしれませんね。


ただ、ちょっと注意点もあって、OR条件というイメージを強く持ちすぎて、Case文を次のようにすると、エラーが表示されてしまい実行できません。


Case "新宿" Or "品川"

このように、「,(カンマ)」で区切るところを「Or」にすると、ダメなんですね。

Excelそのものと、Excel VBAで、少々異なることがありますので、気を付ける必要がありますね。

6/16/2021

Excel。VBA。トラブル頻発。パスワード付きシートの保護のデータをコピーするのは厄介です【Copy trouble】

Excel。VBA。トラブル頻発。パスワード付きシートの保護のデータをコピーするのは厄介です

<Excel VBA>

シートの保護されたデータを範囲選択して、別のシートにコピーして貼り付ける。

何も気にすることもなく出来る処理ですが、Excel VBAをつかってマクロを実行すると、トラブルやらメッセージが表示され、スマートに実行できません。


次の表は、シートの保護を設定しています。


試しに、C8を修正しようとしたら、シートの保護がされているというメッセージが表示されます。


A1からある表を、別シートにコピーするだけですが、頻度が多いので、Excel VBAをつくって対応していきます。


Sub シートの保護コピー ()

    Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

End Sub


プログラム文を説明します。

Range("a1").CurrentRegion.Copy は、A1から始まるCurrentRegion(連続した範囲)をコピーします。


さらに、

Destination:=Worksheets("COPY").Range("a1")

で、COPYというシートのA1に貼り付けます。Destinationは、貼付け先です。


要するに、「A1から連続する範囲をコピーして、シート名COPYのA1に貼り付ける」という意味です。


なんら、問題はありません。

「シートの保護」を設定しないで、実行してみると、問題なくコピーできることが確認できます。


ところが、シートの保護を設定しておくと、どうなるでしょうか?


実行時エラー1004」が表示されてしまいました。

よくみると、シートの保護が設定されていると実行できないとのこと。

シートの保護が設定されただけで、問題なく使えていたマクロが動かなくなるわけですね。


では、どのようにしたらいいのでしょうか?


プログラム文を次のように修正してみましょう。

Sub シートの保護コピー保護解除()

    Worksheets("hogo").Unprotect

    Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

    Worksheets("hogo").Protect

End Sub


コピーする行の前後に、一行追加しました。

Worksheets("hogo").Unprotect

今回元のデータがあるシート名を「hogo」としています。

そのシートに設定されている「シートの保護」を解除するのが、「Unprotectメソッド」です。


コピーした後に、シートの保護を解除しているので、

Worksheets("hogo").Protect

「Protectメソッド」でシートの保護を再設定しています。


それでは、マクロを実行してみましょう。


「実行時エラー1004」も表示されることなく、希望通りに処理することができました。


ところが、シートの保護には、「パスワード」設定ができるわけです。

パスワード付きシートの保護だとどうなるか、確認してみます。


パスワードは「123」としました。


マクロを実行してみると、シートの保護の解除ダイアログボックスが表示されてきました。


パスワードを入力してOKボタンをクリックすれば、無事にコピーすることができますが、いちいち、パスワードを入力するのは面倒です。


そこで、次のように、プログラム文を加筆していきます。


Sub シート保護コピー保護解除()

    Worksheets("hogo").Unprotect Password:="123"

    Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

    Worksheets("hogo").Protect Password:="123"

End Sub


Unprotectメソッドと、Protectメソッドのあとに、Password:="123"というPasswordプロパティを追加することで、パスワード付きシートの保護に対応することができます。


実行して確認すると、コピーも出来ていますし、シートの保護は解除されることなく、しかもパスワードもきちんと設定されていることが確認できましたね。

6/01/2021

Excel。VBA。空白セルを除いたデータを抽出して、別シートにコピーしたい【Copy excluding blank cells】

Excel。VBA。空白セルを除いたデータを抽出して、別シートにコピーしたい

<Excel VBA>

提出日フィールドに、提出日が入力されているデータのみを、別シートにコピーしたいという作業が定期的に行いたいとします。

例えば、次のようなデータがあります。


今回のように、定期的に同じ作業を行うのは、単純作業であればあるほど、面倒なわけです。


作業工程としては、空白を除きたいわけですから、オートフィルターをつかって空白以外を抽出して、抽出されたデータのみを範囲選択して、別シートにコピーする。


そして、元のデータはオートフィルターを設定しているので、解除する。


ということになるわけです。

作業自体はとても、簡単だからこそ、面倒なわけです。


面倒で定期的に行うことがあるとなれば、Excel VBAでマクロを作っちゃいましょう。


次のようなプログラム文を作ってみました。


Sub 空白除外()

    Range("a1").AutoFilter field:=3, Criteria1:="<>"

    Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

    Range("a1").AutoFilter

    Worksheets("COPY").Columns("C:C").ColumnWidth = Columns("C:C").ColumnWidth + 1

End Sub


とりあえず、実行してみましょう。


該当のデータのみが別シートにコピーすることができました。


では、プログラム文を説明してきます。


Range("a1").AutoFilter field:=3, Criteria1:="<>"

AutoFilterメソッドをつかって、オートフィルターを設定します。


今回は、1行目にありましたので、A1のセルをつかっています。


field:=3 は、データの3列目を指しますので、C列の提出日フィールドに対してフィルターを設定していきます。


Criteria1:="<>" のCriteria1は、条件という意味です。

空白以外を抽出したいので、条件には、「"<>"」とすることで、空白以外を抽出することができます。


なお、逆に空白のみを抽出したい場合は、

Criteria1:="="

とすることで、空白のみのデータを抽出することができます。


Criteria1の設定は、間違えやすいので注意が必要ですね。


Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

抽出したデータを範囲選択して、COPYというシート名のA1にコピーします。


「Destination」をつかうことで、貼付け先を指定することができます。


Range("a1").AutoFilter

コピーが終わりましたので、オートフィルターを設定したままにする必要はありませんので、オートフィルターを解除するのが、この一行です。


オートフィルターは、ボタンのオンとオフなので、AutoFilterメソッドを再度つかうだけで、解除することができます。


Worksheets("COPY").Columns("C:C").ColumnWidth = Columns("C:C").ColumnWidth + 1


最後のこの行ですが、別に必要ないといえば必要ないのですが、せっかくなので追加しておくと便利なプログラム文です。


コピーして貼り付けると、文字数が多いと、「#」で表示されてしまいます。


日付は、「2021/12/31」と10文字分の列幅なので、「#」で表示される場合があるかもしれません。


せっかく、楽をするために、Excel VBAでマクロを作ったのに、列幅を自分で調整しなおすのは面倒です。


そこで、列幅を調整しています。


Columns("C:C")は、C列に対して設定します。

ColumnWidth は、列幅を設定するものです。

列幅を、1文字分広げたいので、「ColumnWidth + 1」としております。


なお、列幅を自動調整したい場合には、

Worksheets("COPY").Columns("C:C").AutoFit

とすることで、簡単に列幅を自動調整することができます。


今回のように、単純作業を高速処理できるし、「楽」したいものがあれば、Excel VBAをつかってみるという選択肢もありかもしれませんね。

5/20/2021

Excel。VBA。日報など原版シートから月の日数分コピーしてシート名も日付に変更したい【Sheets name】

Excel。VBA。日報など原版シートから月の日数分コピーしてシート名も日付に変更したい

<Excel VBA>

日報シートを4月なら30シート。
2月だったら28シートをコピーしてしかも、0401のようなシート名に変更するという作業は、単純ではありますが、面倒以外のなにものでもありません。

次のような原版シートがあります。


これをベースとして、月の日数分コピーしたいわけです。

さらに、コピーしたシート名を、B1の営業日である日付と連動させたいわけですね。

しかもシート名は、「0401」のようにゼロ付で表示したいわけです。


これを手動で行うとしたら、そして、毎月発生するとしたら、「キツイ」「アキル」「ダルイ」作業でしかありません。

そこで、Excel VBAでマクロを作ったらどうなるのか、確認してみましょう。

すごく長いプログラム文になると思いきや、結構短いプログラム文で作ることが出来ます。

Sub 原版コピーシート名日付()
    Dim i As Integer
    Dim sheet_name As String
    Dim month_count As Integer
    Dim day_count As Integer
    Dim eigyou_day As Date
    
    eigyou_day = Range("b1")
    month_count = Month(eigyou_day)
    day_count = Day(DateSerial(Year(eigyou_day), month_count + 1, 1) - 1)
    
    sheet_name = Right("0" & month_count, 2) & "01"
    
    For i = 2 To day_count + 1
        
        Worksheets("原版").Copy after:=Sheets(Sheets.Count)
        ActiveSheet.Name = sheet_name
        ActiveSheet.Range("b1") = eigyou_day
               
        sheet_name = Right("0" & month_count, 2) & Right("0" & i, 2)
        eigyou_day = eigyou_day + 1
        
    Next i
    
End Sub

これを実行すると、原版を利用して、月ごとの日数分のシートが挿入されたことが確認できます。

プログラム文を確認していくことにしましょう。
お馴染みの宣言文ですね。
Dim i As Integer
Dim sheet_name As String
Dim month_count As Integer
Dim day_count As Integer
Dim eigyou_day As Date

「sheet_name」には、コピーした後のシート名で使用する変数です。

eigyou_day = Range("b1")
eigyou_dayには、B1の営業日を代入します。

month_count = Month(eigyou_day)
month_countには、B1の日付から「月」を抽出して代入しています。

day_count = Day(DateSerial(Year(eigyou_day), month_count + 1, 1) - 1)
これは、該当付きの末日の数値を算出しday_countに代入しております。

ポイントがあって、翌月1日を
「DateSerial(Year(eigyou_day), month_count + 1, 1」で、求めます。
そして、「-1」すると、先月の月末を算出することができます。

こうすることで、月末が何日なのか問題に対応することができます。
4月だったら、30日というような算出方法を用いると、うるう年に対応することが大変になってしまうので、「翌月-1」というのは、よく使う方法です。


sheet_name = Right("0" & month_count, 2) & "01"

「0401」というように「0付」で桁数を揃えたシート名を作るために、Rightをつかっています。
例えば、10月だったら、「010」として、その右から2文字分を採用するので「10」となるわけです。

ここでは、最初のシートである、「一日目」に対応させています。

For i = 2 To day_count + 1 ~ Next i
繰り返し処理を行います。

これは、原版シートがあるので、原版をコピーするので、2枚目が1日目にあたるので、2からスタートさせています。

そのため、day_countで月末の数値では1つ足らないので、「+1」して多く繰り返し処理をします。


Worksheets("原版").Copy after:=Sheets(Sheets.Count)
「after:=Sheets(Sheets.Count)」で最後尾という指定ができるので、原版シートをシートの最後尾にコピーさせます。

ActiveSheet.Name = sheet_name
ActiveSheet.Range("b1") = eigyou_day
シート名とB1の値を、変更します。

sheet_name = Right("0" & month_count, 2) & Right("0" & i, 2)
次のシート名を準備しています。

「i」を日付で使うことで、1日ずつ増やしたシート名にすることができます。

eigyou_day = eigyou_day + 1
B1に挿入する営業日も「+1」させます。


このように、単純処理でしかも繰り返し同じことをするような場合には、Excel VBAでマクロをつくったらどうなるのかな?と考えてみるといいかもしれませんね。

5/02/2021

Excel。VBA。各シートにあるデータを一つのシートにまとめるのが面倒なので楽をしたい。【Gather on the sheet】

Excel。VBA。各シートにあるデータを一つのシートにまとめるのが面倒なので楽をしたい。

<Excel VBA>

それぞれのシートにあるデータを、一つのシートにまとめる作業は、簡単ですが、シートが多いと、単純作業の繰り返しになるため、とても面倒な処理になってきます。


例えば、次のようなファイルがあります。


新宿・渋谷・池袋・八王子・四谷のシートには、各店舗の売上表が同じセル番地に入力されています。


それを、全店舗シートにまとめたい。

すなわち、各店舗のシートのA1:B11を範囲選択して、全店舗シートにコピーしたいわけです。


作業としては、とても単純ですが、単純がゆえに、繰り返すとなると面倒なわけです。


さらに、各店舗のシートには、シート名でどの店舗なのか管理しているために、店舗名を表示しているデータの列はありません。

単純にまとめてしまうと、どの店舗のデータなのかわからなくなってしまいます。

そこで、全店舗シートのC列に店舗名も入力する作業も追加したい。


そこで、Excel VBAでマクロをつくってしまおうというわけですね。


次のようなプログラム文を用意しました。

Sub データ集合()

    Dim i As Integer

    Dim j As Integer

    Dim data_count  As Long

    Dim total  As Long

    Dim shop_name As String

    

    total = 2


    For i = 2 To Worksheets.Count

        data_count = Worksheets(i).Range("a1").CurrentRegion.Rows.Count - 1

        shop_name = Worksheets(i).Name

        Worksheets(i).Range("a2").Resize(data_count, 2).Copy Worksheets("全店舗").Range("a" & total)

            For j = total To total + data_count - 1

                Cells(j, 3) = shop_name

            Next

        total = total + data_count

    Next

End Sub


まずは、実行してみましょう。


希望通りに、全店舗シートにまとめることができ、C列に店舗名も入力されています。


では、プログラム文を確認していきます。


最初は、変数の宣言ですね。

Dim i As Integer

Dim j As Integer

Dim data_count  As Long

Dim total  As Long

Dim shop_name As String


data_count には、店舗名シートのデータの件数を代入させます。

totalは、全店舗シートにこぴーされたデータの件数で使用します。

shop_nameは、C列用の店舗名のための変数です。


total = 2

これは、全店舗シートの1行目が見出し行であって、データそのものは2行目からコピーしていきますので、2を代入させておきます。


For i = 2 To Worksheets.Count

    data_count = Worksheets(i).Range("a1").CurrentRegion.Rows.Count - 1

    shop_name = Worksheets(i).Name

    Worksheets(i).Range("a2").Resize(data_count, 2).Copy Worksheets("全店舗").Range("a" & total)

        For j = total To total + data_count - 1

            Cells(j, 3) = shop_name

        Next

    total = total + data_count

Next


For i = 2 To Worksheets.Count ~ Next

i=2。

これは、このファイルの左から2番目のシートのデータをコピーしますので、左から2番目の「2」です。

3番目だったら「3」とします。


また、Worksheets.Countで、ファイルの総シート数を算出することができるので、作業したいシート数を繰り返し処理をすることができます。


data_count = Worksheets(i).Range("a1").CurrentRegion.Rows.Count - 1

1行目が見出し行なので、見出し行までコピーするとあとあと、処理が増えてしまうので、見出し行を除いた、データだけの行数を代入しておきます。


shop_name = Worksheets(i).Name

シート名をshop_nameに代入しておきます。


Worksheets(i).Range("a2").Resize(data_count, 2).Copy Worksheets("全店舗").Range("a" & total)

全店舗シートの最終行の下に、店舗シートのデータのみをコピーし貼り付けます。


コピーの範囲は、Range("a2").Resize(data_count, 2)。

A2からデータの件数分が対象とすることで、見出し行を含めないで、コピーすることができます。


For j = total To total + data_count - 1

    Cells(j, 3) = shop_name

Next

貼り付けたデータのC列に店舗名を入力するブロックです。


このようなプログラム文をつくることで、各店舗シートのデータを全店舗シートにまとめることができます。


現場では、単純作業を繰り返して行っているものなどある場合には、Excel VBAでプログラムをつくってみるのもいいかもしれませんね。

4/20/2021

Excel。VBA。オートフィルターでセルの一部が合致するデータを抽出して別シートにコピーしたい。【Copy to sheet】

Excel。VBA。オートフィルターでセルの一部が合致するデータを抽出して別シートにコピーしたい。

<Excel VBA>

大量のデータを読み込んだりした後に、該当するデータを抽出して処理をするということは、よくあります。


作業自体の流れとしては、オートフィルターなどをつかって、データを抽出するわけですね。

ただ、いつも同じ条件で抽出して決まったシートにコピーするという作業は、単純作業になってしまうので、面倒くさい作業になってしまいます。


さらに、抽出条件が、「セルの一部が合致する」ものを抽出したいということで、オートフィルターの抽出設定も面倒となるわけです。


それがこのデータ。


やりたいことは、C列の住所にある「横浜市」のデータを抽出して別のシートにコピーしたいわけです。


オートフィルター機能を使った場合には、「テキストフィルター」にある「指定の値を含む」から、「横浜市」と設定することで抽出してくれます。


「テキストフィルター」の中にはいっての設定ですから、やっぱり面倒ですね。

ところで、「指定の値で始まる」というのは、どのように表現したらいいのでしょうか?


「テキストフィルター」にある、「指定の値で始まる」「指定の値で終わる」「指定の値を含む」は、ワイルドカードを使用することで表現することが出来ます。


横浜市から始まるデータを抽出したいので、ワイルドカードをつかった条件は「横浜市*」とすることで抽出できます。

横浜市以降の複数の文字が含まれていますので「*(アスタリスク)」を使います。


都道府県まで含まれていた場合は、「*横浜市*」とします。

ただ注意しないといけないのは、「中央区」とか「緑区」など、他の地域でも使われていることが想定される場合には、「東京都中央区」とか「横浜市緑区」など範囲を広げて、ワイルドカードを使ってあげる必要があります。


それでは、プログラム文を確認しておきましょう。


Sub ワイルドカードで抽出()

    Range("a1").AutoFilter field:=3, Criteria1:="横浜市*"

    Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

    Range("a1").AutoFilter

End Sub


それでは、実行してみましょう。


このように、「横浜市」から始まるデータを抽出することができました。

それでは、プログラム文を確認しておきましょう。


Range("a1").AutoFilter field:=3, Criteria1:="横浜市*"

AutoFilterでオートフィルターを設定します。

field:=3は、左から3列目ということで、住所の列が対象です。


Criteria1:="横浜市*" で、横浜市という文字で始まるデータが抽出条件です。

ここで、「*(ワイルドカード)」を使います。


Range("a1").CurrentRegion.Copy Destination:=Worksheets("COPY").Range("a1")

Range("a1").CurrentRegion.Copyで、A1から連なっている範囲(CurrentRegion)をコピーします。


Destination:=Worksheets("COPY").Range("a1")は、COPYシートのA1をコピー先とする意味ですね。


Range("a1").AutoFilter

コピーが終了したので、オートフィルターを残しておいてもしょうがないので、オートフィルターを解除するのがこの行です。


たった3行のプログラム文ですが、作業効率を改善できるかもしれませんので、通常作業も、Excel VBAをつかって、マクロを作ってみるといいかもしれませんね。

4/08/2021

Excel。VBA。フォルダー内にある別ファイルのシートを一つのファイルに集めたい【File combination】

Excel。VBA。フォルダー内にある別ファイルのシートを一つのファイルに集めたい

<Excel VBA>

何気ない操作も繰り返す回数が増えると、面倒以外の何物でもありませんね。


例えば、フォルダー内に各店舗のファイルがあって、それぞれのファイルにあるシートを、一つのファイルに集めたい・コピーしたいというのも、その一つだと思います。


今回は、渋谷店・新宿店・品川店のファイルにあるシートを全店舗というファイルに集めたい・コピーするケースでご紹介していきます。


ファイルを開いて、シート名の上で右クリックして、シートのコピーをしていく作業をたった3回かもしれませんが、面倒ですし、ファイル数が増えれば増えるほど、面倒から【苦痛】へと変わっていきます。


全店舗ファイルを開いて、現状を確認しておきます。


集計シートのみしかないことが確認できます。


各店舗のデータも確認しておきましょう。


新宿店のファイルですが、4月シートがあります。他のファイルも同様の構成になっています。


ますは、プログラム文を確認しておきましょう。

Sub ブック結合()

    Dim f_name As String

    Dim path_name As String

    Dim i As Long


    For i = 1 To 1

        path_name = Workbooks(i).Path

    Next j


    f_name = Dir(path_name & "¥*.xlsx")


    Do While f_name <> ""

     Workbooks.Open path_name & "¥" & f_name

       Workbooks(f_name).Worksheets(1).Copy           before:=ThisWorkbook.Worksheets(1)

        ThisWorkbook.Worksheets(1).Name = Replace(f_name, ".xlsx", "")

        Workbooks(f_name).Close False

        f_name = Dir()

       

    Loop

End Sub


説明の前に、実行してみます。

 

このように、あっという間に、一つのファイルに集めることができました。


プログラム文を確認しておきましょう。

Dim f_name As String

Dim path_name As String

Dim i As Long


お馴染みの変数宣言ですね。


For i = 1 To 1

    path_name = Workbooks(i).Path

Next j


For~To~Next文で繰り返しをしていますが、何をしているのかというと、ファイルがあるパス名を取得するためです。

ファイル名だけでは、処理をしてくれません。

そのファイルが、どこのフォルダーにあるのかという情報のパスを取得する必要があるわけです。

変数名のpath_nameは、そのパスを代入するものです。


Workbooks(i).Path

で、そのファイルのあるパスの情報を得ることができます。


確認を続けていきます。

f_name = Dir(path_name & "¥*.xlsx")

先程取得した、パス名と拡張子名を結合させることで、収集元のファイル名を変数名f_nameに代入させます。


なお、マクロファイルの場合には、拡張子は「xlsm」と変更します。

拡張子を設定しておかないと、フォルダー内に関係ないファイルがある場合には、それも該当してしまうので、エラーになってしまいます。


なお、「¥(円マーク)」ですが、半角だと「円マーク」で表示してくれないので「全角の¥」にしておりますので、ご注意ください。


Do While f_name <> "" ~ Loop は、該当するファイルがある間繰り返し処理をするという意味です。


Workbooks(f_name).Worksheets(1).Copy before:=ThisWorkbook.Worksheets(1)

コピー元のシートを、コピー先の1枚目のシートの「before(前:左側)」にコピーします。


ThisWorkbook.Worksheets(1).Name = Replace(f_name, ".xlsx", "")

シート名の重複というわけにはいきませんので、今回は、コピーしたシートのシート名をファイル名に置換する(Replace)作業をしております。


Workbooks(f_name).Close False

開いたファイルを閉じます。


現場には、簡単な処理でも、量が増えると面倒な作業は多いと思いますので、マクロを作ってみるといいかもしれませんね。

3/24/2021

Excel。シートを大量に追加して、指定したシート名に変更するのが面倒なので楽したい。【Add sheets】

Excel。シートを大量に追加して、指定したシート名に変更するのが面倒なので楽したい。

<Excel VBA>

Excelの処理において、とても単純な作業。

例えば、新しいシートを追加して、そのシート名を変更するなんてことは、簡単な作業ではありますが、処理をする量が増えてしまうと、面倒以外の何物でもありません。


単純作業の繰り返しで、面倒に感じる場合には、Excel VBAでマクロを作ってExcelに処理させちゃうことができれば、作業効率も改善するし、自分自身のワーク負荷も緩和することが出来るかもしれません。


そこで、今回は、大量な新しシートを追加して、しかも追加したシートのシート名も指定して変更までするマクロを作っていきます。


意外と短いプログラム文で、出来ちゃいます。


最初に用意するシートがあります。


追加するシート用のシート名のデータを用意します。


今回は、5件分ですが、100件用意すれば100枚の新しいシートを追加することができて、そのシート名も変更することができます。

もしも五十音順にしておきたい場合には、並び替えを事前にしておくとよいかと思います。


作成するExcel VBAのプログラム文を見ていくことにしましょう。


Sub シート作成()

    Dim i As Long

    Dim sheet_name As String

    Dim lastrow As Long

        

    sheet_name = ""

    lastrow = Cells(Rows.Count, "a").End(xlUp).Row

    

    For i = 2 To lastrow

    sheet_name = Worksheets("シート名作成").Cells(i, "a")

    Sheets.Add(after:=Sheets(Sheets.Count)).Name = sheet_name

    Next

End Sub


まずは実行して確認してみましょう。


このように、A列のシート名の順番通りにシートが追加されていることが確認できます。


ちょっとしたことですが、知っていると便利かと思われます。


では、プログラム文を確認しておきましょう。


まずは、お馴染みの変数宣言ですね。

    Dim i As Long

    Dim sheet_name As String

    Dim lastrow As Long

変数名「sheet_name」はシート名を代入しておく変数です。

変数名「lastrow」は、繰り返し数のための変数です。


sheet_name = ""

「sheet_name」を初期化しておきます。


lastrow = Cells(Rows.Count, "a").End(xlUp).Row

シート名の件数を確認するためのプログラム文です。


動きとしては、シートの最終行まで一度行き、そこから、上に向かって移動すれば、データの最終行になるという考え方ですね。


For i = 2 To lastrow ~ Next 文で、繰り返し処理をします。

データが2行目からなので、2から始めて、最終行の行番号まで繰り返します。


sheet_name = Worksheets("シート名作成").Cells(i, "a")

変数「sheet_name」に、シート名を代入するプログラム文ですね。


Sheets.Add(after:=Sheets(Sheets.Count)).Name = sheet_name


Sheets.Addで、シートを追加することが出来ます。

after:=Sheets(Sheets.Count) は、Sheets.Countで、シート数を数えます。


例えば2枚のシートがあれば、Sheets(2)となり、「after」なので、Sheets(2)の右側に挿入されます。


afterを設定しないと、左側にシートが挿入されてしまうので、移動するのが面倒なので、afterの設定をしておきました。


Name = sheet_name で、シート名を設定することができます。


比較的、わかりやすいプログラム文ではありますが、大量のシートを挿入しないといけない時などに知っておくといいかもしれませんね。

3/09/2021

Excel。VBA。読み込んだデータで、月が替わる行に小計行を追加したい【The end of the month】

Excel。VBA。読み込んだデータで、月が替わる行に小計行を追加したい

<Excel VBA>

データを読み込んだら、ついでに、面倒な処理を自動的に行わせることができたら便利ですよね。


例えば、次のようなデータを読み込んだとします。


読み込んだ後に自動的に、月が替わったら、行を追加して、追加した行に月ごとの小計を算出したいとします。

こんな感じですね。


月が替わるところを見つけて、行を挿入して、さらに、月ごとの小計を算出するだけですが、イチイチこの作業を繰り返すのは面倒ですね。


しかも、月ごとのデータの件数が異なっていれば、SUM関数などの引数の範囲をその都度設定するのは、さらに面倒です。


そこで、Excel VBAでマクロを作ってしまうと、大幅に時短できますし、何より楽ができます。


色々なプログラム文があるとは思いますが、次のように作ってみました。


Sub 月別小計()

    Dim i As Long

    Dim mon As Integer

    Dim n_mon As Integer

    Dim sub_t As Long

        

    i = 2

    mon = Month(Range("a2"))

    sub_t = 0

    

    Do While Cells(i, "a") <> ""

       n_mon = Month(Cells(i, "a"))

       

       If mon = n_mon Then

            sub_t = sub_t + Cells(i, "b")

       Else

            Cells(i, "a").EntireRow.Insert

            Cells(i, "a").Value = mon & "月合計"

            Cells(i, "b").Value = sub_t

            sub_t = 0

            mon = n_mon

       End If


       i = i + 1

       

       If Cells(i, "a") = "" Then

            Cells(i, "a").Value = mon & "月合計"

            Cells(i, "b").Value = sub_t

            i = i + 1

       End If

    Loop

End Sub


それでは、説明していきます。

お馴染みの変数宣言の文からスタートですね。

Dim i As Long

Dim mon As Integer

Dim n_mon As Integer

Dim sub_t As Long


monは、今月の値をいれる変数です。

n_monは、次のデータの月をいれる変数です。

sub_tは、月ごとの小計を算出する時に使う変数です。


初期値を設定します。

i = 2

mon = Month(Range("a2"))

sub_t = 0

    

mon = Month(Range("a2")) は、最初のデータの月を代入しています。


Do While Cells(i, "a") <> "" ~ Loop をつかって繰り返し処理をしています。


Do While Cells(i, "a") <> "" は、A列の値が空白でないという意味ですね。

つまり、データが無くなるまで繰り返し処理を行いたいわけです。


繰り返し処理のDo Whileの中を確認していきましょう。


n_mon = Month(Cells(i, "a")) は、次のデータの月の値を代入しています。


月替わりしたかどうかを確認するために、If~Then~Else~End Ifで条件分岐させた処理をします。


If mon = n_mon Then で、今までの月の値と、このデータの月の値を比べます。


もしも、同じだったらば、

sub_t = sub_t + Cells(i, "b") で、小計の合計値に、このデータの売上高のデータを追加していきます。


Else で、月の値に違いが発生している場合、つまり月が替わった時の処理を行います。

Cells(i, "a").EntireRow.Insert で、該当のセルの下側に空白行を追加することができます。


Cells(i, "a").Value = mon & "月合計" は、追加した行に、「○月合計」という見出しを入力させています。


Cells(i, "b").Value = sub_t は、月ごとの合計値を入力しています。


次の2行は、月が替わったので、初期化の作業をしています。

sub_t = 0

mon = n_mon


次の行のデータを参照させるための作業が、i = i + 1 です。


これで、メインのプログラムは終わっているのですが、このまま実行すると、空白行まで繰り返すという処理なので、最終月。

今回は、12月にあたりますが、その合計値の行を表示する作業を行わないで終了してしまいます。


そこで、次のプログラム文をいれました。

If Cells(i, "a") = "" Then

    Cells(i, "a").Value = mon & "月合計"

    Cells(i, "b").Value = sub_t

    i = i + 1

End If


これで、月が替わった時に、自動的に空白行を追加してその追加した行に月ごとの合計値を算出することができました。


このように、ちょっとしたプログラム文でも作業効率を改善できるかもしれませんので、Excel VBAで対応できそうなところがあれば、少しずつでも移行していくといいのかもしれませんね。

2/25/2021

Excel。VBA。読み込んだデータに001のような「0(ゼロ)」付き連番を楽してつくりたい【With zero】

Excel。VBA。読み込んだデータに001のような「0(ゼロ)」付き連番を楽してつくりたい

<Excel マクロ>

大したことない処理ほど、イチイチ設定するのは、かえって面倒に感じるものです。

まして、頻繁にその処理を行うとなると、より一層面倒に感じたりします。


例えば、次のようなデータを読み込んだとします。


B列の顧客名だけでは、管理上不便なので、A列のNOには、連番を設定したいわけです。

そして、できれば、次のような「0(ゼロ)」付の連番にしたい。


大した処理ではないんですね。

連番は、オートフィルで設定したら、表示形式のユーザー定義で、ゼロ付数値に表示するようにすればいいわけですね。


だけど、これが、マクロをつくることで、すぐに設定できるならば、楽になります。


では、どのようにしたら、ゼロ付数値の連番を設定することができるのでしょうか?


Excel VBAのプログラム文をつくってみましょう。

Sub zero連番()

    Dim i As Long

    Dim lastrow As Long

       

    lastrow = Cells(Rows.Count, "b").End(xlUp).Row

    

    For i = 2 To lastrow

      Cells(i, "a").NumberFormat = "000"

      Cells(i, "a").Value = i - 1

    Next

End Sub


実行して、確認してみると、A列のNOにゼロ付数値の連番が作成することができました。


このプログラム文を説明していきます。


最初はお馴染み、変数の宣言です。

Dim i As Long

Dim lastrow As Long


lastrow = Cells(Rows.Count, "b").End(xlUp).Row

B列のデータの最終行番号を取得するための行ですね。

lastrowという変数に代入させています。


そして、連番を設定するので、繰り返し処理を作っていきます。

それが、For i = 2 To lastrow ~Next です。


データそのものは、2行目から始まっているので、「2」から開始させています。


そして、

Cells(i, "a").NumberFormat = "000"

このプログラム文が、今回のポイントとなる行です。


NumberFormatプロパティをつかうことで、表示形式を設定することができるようになります。


ゼロ付数値にしたいわけですから、通常のExcelの表示形式と同じように、「000」とすることで、ゼロ付数値で表示することができるわけです。


Cells(i, "a").Value = i - 1

この行は、連番を設定するための一行です。

1からの連番にしたいので、「i-1」とした数値を代入させています。


今回は、

Cells(i, "a").NumberFormat = "000"

Cells(i, "a").Value = i - 1

という順番でしたが、逆にしても、ゼロ付数値で連番を設定することができます。


なお、ゼロ付数値にしない場合には、

Cells(i, "a").Value = i – 1

でOKです。


NumberFormatプロパティは、データを読み込むプログラム文に組み込んで使うことで、データを読み込んだ後の表示形式の設定をしないで済みます。

使ってみると結構便利なプロパティではないかと思います。


現在使用しているプログラムに組み込んでみると、作業効率化より改善できるかもしれませんね。