実務で最もやる機会が多いであろう、テーブル内の特定データをコピーするやり方を解説します。ここでは、下図のテーブルから、[名前]が"田中"であるデータだけを、別のシートにコピーしてみます。なお、コピー元のテーブルはアクティブシートにあり、コピー先はSheet2とします。
Sub Sample1() With Range("A1").ListObject.Range .AutoFilter 2, "田中" .Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
【実行結果】
Sub Sample2() With Range("A1").ListObject.DataBodyRange .AutoFilter 2, "田中" .Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
【実行結果】
コピー先で、[日付]列の列幅は自動調整されません。コピー元の[日付]列の列幅を広げておいたので、コピー先も私が手動で列幅を調整しました。
いずれにしても、テーブルに設定されている縞々の書式が一緒にコピーされます。この書式、いらいないときもあります。むしろ、よくあります。特に実データだけ、別の表に集約するような場合、まさに"余計なお世話"です。縞々の書式をつけないでコピーすることはできませんから、ここは苦肉の策です。コピーする前に、テーブルをスッピンの"書式なし"にしてからコピーし、コピーが終わったら元の書式に戻してやります。テーブルに設定されている書式は、TableStyleプロパティで分かります。TableStyleプロパティには、設定する書式の名前を文字列で指定します。TableStyleプロパティに空欄("")を指定すると、書式は"なし"になります。詳しくは「セル範囲をテーブルにする」をご覧ください。
Sub Sample3() Dim A As String With Range("A1").ListObject A = .TableStyle .TableStyle = "" With Range("A1").ListObject.DataBodyRange .AutoFilter 2, "田中" .Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With .TableStyle = A End With End Sub
【実行結果】
テーブル全体ではなく、特定の列だけコピーするには、テーブルのListColumnをコピーしてやります。
Sub Sample4() ''タイトル行ごとコピーするとき With Range("A1").ListObject .Range.AutoFilter 2, "田中" .ListColumns(2).Range.Copy Sheets("Sheet2").Range("A1") .ListColumns(4).Range.Copy Sheets("Sheet2").Range("B1") .Range.AutoFilter 2 End With End Sub
Sub Sample5() ''実データだけコピーするとき With Range("A1").ListObject .Range.AutoFilter 2, "田中" .ListColumns(2).DataBodyRange.Copy Sheets("Sheet2").Range("A1") .ListColumns(4).DataBodyRange.Copy Sheets("Sheet2").Range("B1") .Range.AutoFilter 2 End With End Sub
テーブルのコピーでは、変な現象が起こります。なぜそうなるのか、そうすべきなのか、合理的な理由が思いつきませんので、これはバグでしょう。しかも、けっこう致命的なバグです。Microsoftも、そのうち修正するでしょう。まずは、その変な現象をご覧ください。
上記の状態で、先の
Sub Sample1() With Range("A1").ListObject.Range .AutoFilter 2, "田中" .Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
を実行します。実行結果は下図のようになります。
では、同じコードを次の状態で実行します。
実行結果は下図のようになります。
オートフィルタで絞り込んだ結果だけでなく、すべてのデータがコピーされてしまいました。さらにコピー先もテーブルになります。違いが分かりますか?実行前の状態を、よ~く見てください。正解はCMの後で…
オートフィルタで絞り込んだ結果だけが正しくコピーされたときは、アクティブセルがテーブル内にありました。対して、オートフィルタの結果が無視されて、すべてのデータがコピーされてしまったときは、アクティブセルがテーブルの外にありました。その違いです。これを発見したのは、本当に偶然です。マクロを実行するとき、アクティブセルの位置によって動作が異なるなんて、今までのExcelで私には記憶がありません(ホントは、いくつかありますけど、どれも理由が分かるものばかりです)。ちなみにこれ、DataBodyRangeだったらうまくいくんです。ますますもって、そうする理由が思いつきません。
じゃ、今までワークシート上でやってきた方法でコピーしてみましょう。通常のワークシート上では、テーブルのRangeとかDataBodyRangeみたいなのはありませんから、CurrentRegionを使いました。
Sub Sample6() With Range("A1") .AutoFilter 2, "田中" .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
結果は同じです。CurrentRegionで「タイトル行を含む全データ」をコピーするとき、アクティブセルがテーブルの外にあると、絞り込んだ結果だけでなく、全データがコピーされてしまいます。いや、待ってください。もしかしたら、今までもそうだったのでしょうか?通常のワークシート上で、オートフィルタの結果をコピーするときも、アクティブセルによる動作の違いがあったのでしょうか。テーブルを通常のセル範囲に変換して、上記のマクロを実行してみます。結果はどうなるでしょうか!詳しくはWebで!(あ、ここか…)。
テーブルではなく、通常のセル範囲で試したところ、上記のようにCurrentRegionを使うと、アクティブセルが表の中にあろうと外にあろうと、どちらも正常に絞り込まれた結果だけがコピーされます。変な現象が起こるのは、テーブルのときだけです。ここからは推測です。
さて、ではどうするかと。そもそも、オートフィルタで絞り込まれた結果を普通にコピーして、非表示のセルを除いて表示されているセルだけがコピーされるのは、Excelの親切機能です。Excel 2000までは違いました。Excel 2002で(何のアナウンスもなく)変わったんです。Excel 2000までは、ひとかたまりのセル範囲をコピーすると、非表示のセルも一緒にコピーされてしまいました。何度心の中で「使えねぇ~」とつぶやいたことでしょう。そこでExcel 2000までは、必ず可視セルを指定していました。
Sub Sample6() With Range("A1") .AutoFilter 2, "田中" .CurrentRegion.SpecialCells(xlCellTypeVisible).Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
今回のテーブルには、この手が通用するのでしょうか。
Sub Sample7() With Range("A1").ListObject.Range .AutoFilter 2, "田中" .SpecialCells(xlCellTypeVisible).Copy Sheets("Sheet2").Range("A1") .AutoFilter 2 End With End Sub
うまくいきました。SpecialCellsを絡めることで、アクティブセルがテーブルの外にあっても、正しく絞り込まれた結果だけがコピーされました。当面は、この手で乗り切るしかありませんね。
冒頭で"致命的なバグ"といったのは、アクティブシートではない、別のシート上にあるテーブルをオートフィルタで絞り込み、その結果を別のシートにコピーするケースです。これ、実務ではよくあります。よろしいですか、アクティブシートではないシート上のテーブルを操作するということは、絶対にアクティブセルはテーブルの中に存在しません。だって、アクティブセルはアクティブシートにしかないのですから。したがって、アクティブシートではないシート上のテーブルからコピーするときは、必ず全データがコピーされちゃうってことです。あり得ないです…。まだ、VBAでテーブルを操作する人は少ないでしょうから、今のうちにコッソリ、ProPlusの月次チャネル更新で直して欲しいです。ああ、もちろん、直したときはちゃんと告知して欲しいです。ある日急に、ってのはかんべんしてください。