🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
VBA

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

コピー

元のオブジェクトを破壊することなく、オブジェクトの複製を生成することをコピーと呼びます。

フィルタ

フィルタとは、特定の条件に合わせてデータへのアクセスをブロックするプログラムやルーチンを指します。

マクロ

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

Q&A

解決済

4回答

978閲覧

シートを初期化する前後でマクロの結果に差がある

sa3sa3sa3

総合スコア5

VBA

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

コピー

元のオブジェクトを破壊することなく、オブジェクトの複製を生成することをコピーと呼びます。

フィルタ

フィルタとは、特定の条件に合わせてデータへのアクセスをブロックするプログラムやルーチンを指します。

マクロ

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

0グッド

0クリップ

投稿2019/12/13 02:12

以下をマクロで実現しています。
①ExcelBookに格納されたデータをコピー・オブジェクトに格納する
②別ExcelBookに貼り付ける
③年度でフィルタをかける
④表示されているデータを削除する
⑤フィルタを解除する

その後、初期化(貼り付けしたデータを削除)して、再度マクロを実行(①~⑤)すると、
初回と異なる結果となります。

初回結果
■フィルタ前データ件数(iAllCount):0
■フィルタ後データ件数(iCount):0
■■フィルタ前データ件数(iAllCount):705
■■フィルタ後データ件数(iCount):357
初期化後の結果
■フィルタ前データ件数(iAllCount):0
■フィルタ後データ件数(iCount):0
■■フィルタ前データ件数(iAllCount):348
■■フィルタ後データ件数(iCount):541

初回結果と初期化後の結果の「■■フィルタ前データ件数(iAllCount)」の差が357で、
初回の「■■フィルタ後データ件数(iCount)」と一致するため、
初期化漏れではないかとデバッグ等しているのですが、原因が特定できません…。

何か解決するためのヒントになるようなことがあればご教授いただけないでしょうか。
よろしくお願いいたします。

VBA

1Function filterNendo() As Integer 2 3 Dim rangeAll As Range 'フィルタ対象データ 4 Dim iAllCount As Integer 'フィルタ前データ件数(全件) 5 Dim iCount As Integer 'フィルタ後データ件数(削除分) 6 Sheet2.Select 7 8 MsgBox "■フィルタ前データ件数(iAllCount):" & iAllCount 9 MsgBox "■フィルタ後データ件数(iCount):" & iCount 10 11 'シート1の年度と一致しないデータを表示 12 Sheet2.Range("J2").AutoFilter Field:=59, Criteria1:="<>" & Sheet1.Range("C15").Value 13 Set rangeAll = Sheet2.AutoFilter.Range 14 iAllCount = rangeAll.Rows.Count + 1 15 iCount = WorksheetFunction.Subtotal(3, Columns(59)) - 1 16 17 MsgBox "■■フィルタ前データ件数(iAllCount):" & iAllCount 18 MsgBox "■■フィルタ後データ件数(iCount):" & iCount 19 20 On Error Resume Next 21 Sheet2.Rows("3:" & iAllCount).SpecialCells(xlCellTypeVisible).Delete 22 On Error GoTo 0 23 24 If Sheet2.FilterMode = True Then 25 Sheet2.ShowAllData 26 End If 27 28 '廃棄処理 29 Set rangeAll = Nothing 30 Set AutoFilter = Nothing 31 32 filterNendo = iCount 33 34End Function

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

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

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

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

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

guest

回答4

0

提示のコード、
多分、コード自体は問題ないだろうと思います。
が、表現が(操作手順が)、回りくどいです。
回りくどい表現なので、紛れ(不具合)が起きやすいのかも知れないので、
少し怖いかなという印象です。

例えば、
>iCount = WorksheetFunction.Subtotal(3, Columns(59)) - 1
とか、
>Sheet2.Rows("3:" & iAllCount).SpecialCells(xlCellTypeVisible).Delete
これでも、間違いないのかも知れませんが、、、、

えっと、オートフィルターを使うときの注意点として、
「シートがオートフィルターモードの時は、不可視のセルは操作対象から外れる。」
ということです。
なので、敢えて、
SpecialCells(xlCellTypeVisible).Delete
とSpecialCellsメソッドでセル範囲をさらに指定しなおすみたいなことはしなくていいです。

逆に、抽出件数を数える場合は、

.Columns(1).SpecialCells(xlCellTypeVisible).Count

みたいな感じで、列を限定してからSpecialCellsメソッドで、セルの個数を数えることで、
分かります。

そこで、えっと、、、、
プログラムがすんなり動けば、
提示のコードでいいのだろうと思いますが、
途中でエラーなどで止まった後、
オートフィルターが掛かったままで「初期化した」と思い込んでいた時に、
2回目で結果がおかしいということが起きます。

なので、この関数内で必ず、きっちり、結果を書き込むシートを「初期化」する
ようにしてはいかがでしょうか?

ExcelVBA

1Option Explicit 2 3 4Function myFilter(ByVal sKeyWord As String, _ 5 ByRef rngOld As Range, _ 6 ByRef rngNew As Range) As Long 7 Dim i As Long 8 9 rngNew.Worksheet.UsedRange.ClearContents 10 rngOld.Copy rngNew(1) 11 12 rngNew.AutoFilter Field:=59, Criteria1:="<>" & sKeyWord 13 With rngNew.Worksheet.AutoFilter.Range 14 i = .Columns(1).SpecialCells(xlCellTypeVisible).Count - 1 15 If i > 0 Then .Offset(1).EntireRow.Delete 16 .AutoFilter 17 End With 18 19 myFilter = i 20End Function 21 22Sub test() 23 Dim i As Long 24 i = myFilter("2010", Worksheets(1).Range("A1").CurrentRegion, Worksheets(2).Range("A1")) 25 26 MsgBox i & " 件削除" 27End Sub 28

件数で何かを見つけるというより、
オートフィルターの対象セル範囲が、ちゃんと正しく(=思い通りに)設定されているかを、
ステップインをしてみながら、シート上の状態を確認しながら、
場合によってはイミディエイトウィンドウやウォッチ式なども利用しながら、
デバッグしてみてください。

最後になりましたが、
あえて、全部をコピーしてから不要な行を削除しなくても、
シート1の表で必要な物を抽出し、
コピペすれば、手順が少なくて済みます。

ExcelVBA

1Function myFilter2(key As String, rngFrom As Range, rngTo) As Long 2 Dim i As Long 3 4 rngTo.Worksheet.UsedRange.ClearContents 5 With rngFrom 6 .AutoFilter 59, "<>" & key 7 i = .Columns(1).SpecialCells(xlCellTypeVisible).Count 8 If i > 1 Then .Copy rngTo 9 .AutoFilter 10 End With 11 myFilter2 = i - 1 12End Function

投稿2019/12/14 04:10

mattuwan

総合スコア2163

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

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

sa3sa3sa3

2019/12/16 01:28

回答ありがとうございます。 VBA初心者なもので、デバッグが不十分でした…。ステップインで1回目・2回目実施してみたところ、  Sheet2.Range("J2").AutoFilter Field:=59, Criteria1:="<>" & Sheet1.Range("C15").Value で2回目が348行目までしかフィルタ範囲になっていないことがわかりました。 (348行目までは年度(BG列)でフィルタリングされています。) ここを解決できればうまくいきそうな気がします…。 ※ コピー元のデータなのですが、別ExcelBookにあります。 データ数が多くレスポンスが気になるので、一旦オブジェクトに保持し、そこから転記しています。 (コピー元のデータは多処理でも使用しているので、編集することができません。)
guest

0

###質問

VBAのコード見ましたが、filterNendo関数は問題ないと思います。
①~⑤のロジックを1回のトリガで実施するマクロを組まれていると思いますが、
1回目の②終了後と2回目の②終了後で全く同じデータがsheet2に出来上がっているか確認して頂きたいです。(全く同じデータなら、結果は同じになると思います)

以下フロー

1回目実行
② sheet2にデータコピー(J2:BP) 705件
【質問1】この時のsheet2の状態は1回目と2回目で同じですか??(この状態で処理を止めて、確認してほしいです。)
⑤ フィルタ処理(filterNendo関数)
(1)705件のデータをsheet1の年度情報でフィルタ➡357件表示
(2)表示されている357件を削除する➡0件表示
(3)フィルタを解除する(ShowAllData)➡348件表示(=705-357)
1回目終了

【質問2】終了時に348件データが残りますが、残った348件を手動で消すのでしょうか??

###補足
ttyp03さんとのやり取りを見させて頂きましたが、Field59はJ列を基準にして59列分だと思います。そのため、Field:=59はBPではないかと思います。sheet2の年度情報はBP列に記載されていると思うのですが...

>>Field:=59にあたる"BG2"を使用し...

以上です。

投稿2019/12/13 09:09

編集2019/12/13 09:18
KazuSaka

総合スコア640

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

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

sa3sa3sa3

2019/12/16 00:56

回答ありがとうございます。 【質問1】③④⑤の処理(filterNendo)を実行しない場合、1回目と2回目では同じ結果となりました。 【質問2】初期化マクロを作成していて、データがあるセルをClearContentsで初期化しています。手動(Deleteキー)で初期化した場合も、同様の事象(正しく動作しない)が生じました。 【補足】③の処理で止めると、年度(BG列)でフィルタリングされていました。 記載いただいた検証(filterNendoを実行せず比較する、初期化について見直す)で、原因が③④⑤にありそうだと改めて思いました。 filterNendoのメソッドをもう少し解析してみようと思います…。
KazuSaka

2019/12/16 04:56

解決したようでよかったです。 autofileterのfield引数はリストの最初の列をfield:1とカウントすると思いますが... 以下のサイトにもそのような記載が... (フィルタの対象となるフィールド番号を整数で指定。表の左端列が「1」 https://www.sejuku.net/blog/38849 まあ大丈夫です。
guest

0

提示されているコードは③に該当するコードと思われます。
件数に影響がある箇所は以下の行のみです。

VBA

1Sheet2.Range("J2").AutoFilter Field:=59, Criteria1:="<>" & Sheet1.Range("C15").Value

更に詳細に突き詰めると、動的に影響するのは、以下の2箇所です。

  • Range("J2")
  • Sheet1.Range("C15").Value

これらは恐らく①②の処理で生成されると思われますので、そのあたりを見てみてはいかがでしょうか。
..

投稿2019/12/13 04:44

編集2019/12/13 04:50
ttyp03

総合スコア17000

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

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

sa3sa3sa3

2019/12/13 05:51

早速の回答ありがとうございます。 記載いただいた2か所についてですが、 •Range("J2")  msgboxでデバッグしてみましたが、1回目・2回目とも、同じ結果となりました。  「Sheet2.Range("J2").AutoFilter」で、フィルタ範囲を指定しているつもりなのですが、  使用方法が誤っているのでしょうか?  ※Field:=59にあたる"BG2"を使用し、「Sheet2.Range("BG2").AutoFilter」としても   結果は変わりませんでした。 •Sheet1.Range("C15").Value  msgboxでデバッグしてみましたが、1回目・2回目とも、同じ結果となりました。
ttyp03

2019/12/13 05:56

Range("J2")から始まる範囲をフィルタ化するという意味だと思うので、その範囲が違う(行数が違う)んじゃないかなと推測しています。 MsgBoxでデバッグできることではないです。 Sheet1.Range("C15").Value こちらは問題なさそうですね。
sa3sa3sa3

2019/12/13 07:16

Range("J2") はフィルタする範囲の開始セルで、タイトル行を指定しています。 msgboxで1回目・2回目ともタイトルが取得できていることを確認できたので、問題はないと思っているのですが…。 ④で表示されているデータ(タイトル行は除く)が削除でき、見た目上は1回目の処理前と同じ状態であることを確認しているのですが、結果が異なる理由がわかりません…。 もしほかに何かお気付きの点があればご教授お願いいたします。
ttyp03

2019/12/13 08:04

J2はあくまでもフィルタ開始位置であって、そこから下方向にどれだけデータ量があるかで結果が違ってくると思います。 そこに差があるのでは?と思っているのですが。
sa3sa3sa3

2019/12/16 00:46

返信ありがとうございます。 ③④⑤の処理(filterNendo)をしない状態で1回目と2回目のデータを比較してみましたが、一致していました…。 filterNendoのメソッドを1文ずつ解析してみようと思います…。
guest

0

自己解決

デバッグ(ステップイン)により
Sheet2.Range("J2").AutoFilter Field:=59, Criteria1:="<>" & Sheet1.Range("C15").Value
で2回目が348行目までしかフィルタ範囲になっていないことがわかりました。
前回処理の結果がフィルタ範囲に影響していたようなので、filterNendoメソッドの最後にオートフィルタの解除を入れることで解決しました。

皆様、ありがとうございました!

投稿2019/12/16 02:28

sa3sa3sa3

総合スコア5

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問