🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA で Sort を使う — Range.Sort と Sort オブジェクト(元の並び順を取り戻す方法)

|

Excel VBA で Sort を使う — Range.Sort と Sort オブジェクト(元の並び順を取り戻す方法)

TL;DR — コードでのソートは恒久的な並べ替えです。マクロが動いたあとに Ctrl+Z はありません。だから .Sort が発火した時点で、保存しておかない限り元の行順は消えています。 元に戻す必要が出るかもしれないなら、ソートするインデックス列(1, 2, 3…)を足して おき、あとでそれで並べ直せるようにします。Header:=xlYes は必ず渡してください。さもないと Excel が見出し行をデータの中へソートしてしまうかもしれません。そしてレコード全体を ソートすること — 単一列ではなく CurrentRegion で表全体を。さもないと 1 列だけ並べ替えて、 残りをぐちゃぐちゃにしてしまいます。

' 表を C 列(範囲内で 3 列目)で降順にソートする - レコード全体が一緒に動く。
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sales")
With ws.Range("A1").CurrentRegion               ' 表全体。だからすべての列がそろったまま
    .Sort Key1:=.Columns(3), Order1:=xlDescending, Header:=xlYes
End With

ソートは Excel で最も安全な操作に見えます — 手作業では 1 日に何百回もやり、キー 1 つで 取り消せます。コードでは、その安全網は消えています。VBA のソートについて理解すべき最も 大事なことは構文ではありません — マクロの中のソートは、気軽には元に戻せないミューテーション だ、ということです。以下はすべて、まずその事実からデータを守ること、それから仕組み、という 順で組み立てられています。

この記事で学べること

  • 考え方の軸 — ソートは恒久的なミューテーションであり、それをどう可逆にするか
  • データを救うたった 1 つのルール — レコード全体をソートし、1 列だけは決してソートしない
  • Header:=xlYes — 省略するとなぜ見出しがデータに落ちるのか
  • Range.Sort(手軽・キー 3 つまで)と Sort オブジェクト(冗長・無制限・永続)
  • SortFields.Clear の罠 — 前回の実行からソートキーを引き継ぐ
  • 文字列として保存された数値が 1, 10, 2 と並ぶ理由 — そしてその直し方

考え方の軸:ソートはミューテーションであってビューではない

前回のガイドは AutoFilter をビュー — 可逆で、何も恒久的には 隠さない — として枠付けしました。ソートはその逆です — ミューテーション。行の順序を 物理的に書き換えます。下に隠れた「元の並び」があって復元できる、ということはありません。 そして決定的なのは、マクロがソートしたあとは Application.Undo が効かないことです — VBA を走らせると Excel の取り消しスタックがクリアされ、ユーザーも Ctrl+Z を押せません。

これが、すべてのソートの前に考えるべきことを変えます — この並びが大事なら、自分で保存 しなければならない。 安上がりで鉄壁の方法はインデックス列です — ソートの前に行に 1, 2, 3… と刻印しておけば、「元の順に戻す」はその列でのソートにすぎません。

' ソートの前に「並びを戻すための列」を追加する。
Dim i As Long, lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastRow
    ws.Cells(i, "Z").Value = i - 1        ' 空いている列に 1, 2, 3...
Next i
' ...あとは好きにソート。戻すには Z 列を昇順にソートする。

元の順が必要になりうるどんなソートでもこの習慣を身につければ、「取り消しがない」問題は 消えてなくなります。

最も大事なルール:1 列ではなくレコード全体をソートする

これがデータを静かに破壊する間違いです。1 列を選んでソートすると、Excel はその列だけを 並べ替え、ほかのすべての列を元の場所に残します。名前がいまや違う電話番号と並び、請求書 ID が 違う金額を指します。何もエラーは出ません — シートが静かに、そして恒久的に間違いになるだけです。

' 間違い - B 列だけをソートし、各行の残りから引きはがす。
ws.Range("B2:B100").Sort Key1:=ws.Range("B2"), Order1:=xlAscending

' 正しい - 表全体をソート。すべての列が自分の行と一緒に移動する。
ws.Range("A1").CurrentRegion.Sort Key1:=ws.Range("B2"), Order1:=xlAscending, Header:=xlYes

ルールはこうです。それでもなおレコードとして完結する、最小の範囲をソートする。 実際には それは表全体を意味します — Range("A1").CurrentRegion が連続したブロックをつかんでくれます。 ソートのキーは単一列でかまいません。ソートの範囲はレコードのすべての列にまたがらなければ なりません。これは、1 つのセルだけを削除するのではなく行全体を削除するのと、まったく同じ 整合性の教訓です — レコードの一部を並べ替えれば、その全部を壊します。

Header:=xlYes を指定しないと見出しがデータに紛れ込む

Range.Sort には Header 引数があり、その既定値は xlGuess です — Excel は先頭行が見出しか どうかを推測しようとします。推測を外すと、あなたの列見出しはほかの行と同じようにデータの 中へソートされ、どこか真ん中に着地します。決して推測させてはいけません。

.Sort Key1:=.Columns(1), Order1:=xlAscending, Header:=xlYes   ' 先頭行は見出し - 動かさず残す

範囲が見出し行を含むとき(通常のケース)は Header:=xlYes を、純粋なデータのときは xlNo を 渡します。このたった 1 つの引数が、あの古典的な「見出しが 43 行目に行ってしまった」バグを 防ぎます。

2 つのソート方法:Range.Sort と Sort オブジェクト

VBA は 2 つの API を用意していて、簡潔さと機能を引き換えにします。

Range.Sort は手軽なほうです — 最大3 つのソートキー(Key1/Key2/Key3)を持つ 1 文の ステートメント。よくあるケースにぴったりです。

' Region(昇順)で、次に各 Region 内で Amount(降順)でソートする。
ws.Range("A1").CurrentRegion.Sort _
    Key1:=ws.Range("B1"), Order1:=xlAscending, _
    Key2:=ws.Range("C1"), Order2:=xlDescending, _
    Header:=xlYes

Worksheet.Sort オブジェクトは冗長なほうです — SortFields を 1 つずつ追加します — が、 3 キーの上限を取り払い、Range.Sort にはできないセルの色・フォントの色・アイコンによる ソートに対応します。さらに永続すること — ソート設定がシートに貼り付く — が、まさに鋭い刃を 持つ理由です。

With ws.Sort
    .SortFields.Clear                       ' <-- 重要: まず残っているキーを捨てる
    .SortFields.Add Key:=ws.Range("B2:B100"), Order:=xlAscending
    .SortFields.Add Key:=ws.Range("C2:C100"), Order:=xlDescending
    .SetRange ws.Range("A1").CurrentRegion
    .Header = xlYes
    .Apply
End With

Sort オブジェクトを定義する罠 — SortFields.Clear を最初に置かなければなりません。 ソート フィールドはワークシートに永続するので、前回のマクロ実行 — あるいは手でソートしたユーザー — が残したキーがまだそこにあります.Clear を飛ばすと、古いキーと新しいキーを合わせた、 あなたが頼んでもいない混合でソートしてしまいます。私のルールはこうです。3 キーまでのすべてに Range.Sort を選び、本当にもっと多くのキーか色/アイコンのソートが必要なときだけ Sort オブジェクトへ移る — そしてそのときは、With の中の最初の 1 行が SortFields.Clear です。

データ型の罠:文字列として保存された数値

「変な順序で出てくる」ソートは、ほぼ必ずデータ型の問題です。数値の列が実は文字列として 保存されていると — CSV や Web からのインポート後によくあります — Excel はそれを辞書順に、 1 文字ずつソートします — "1"、"10"、"2"、"21"、"3"。値は数値に見えるのに、言葉のように並ぶのです。

直し方は上流にあります — ソートの前に列を本物の数値に変換すれば、210 の前に並びます。 それは変換の仕事です — インポートしたテキストを本物の数値型に変えるには CStrCDateVal を参照してください。列が本物の数値を持てば ソートは正しくなります。ソート引数をいくらいじっても、数値のふりをした文字列は直りません。

ExcelMaster の活用

ソートは、なじみ深く親しみやすい操作の裏にその危険を隠します — 見出しを埋めてしまう Header:=xlYes の書き忘れ、レコードをぐちゃぐちゃにする単一列ソート、古いキーを引き継ぐ SortFields.Clear の 欠落、文字列としての数値の順序、そして何より、マクロが動いたあとは取り消しがないという事実。 どれもエラーを出しません — ただ、データを誤って並べ替えたまま、取り返しのつかない状態に するだけです。

ExcelMaster なら、望むソートを 説明するだけです。「地域で、次に金額の高い順でソートして」と言えば、レコード全体を(1 列では なく)ソートし、Header を明示的に設定し、古いソートフィールドをクリアし — そして先にシートを バックアップするので、Excel 自身の取り消しが消えていても戻り道を残します。ブックもコードも あなたの手元に残り、1 列ソートがすべての名前をその数字から静かに切り離す、あの一幕だけを 飛ばせます。

よくある質問

VBA マクロがやったソートを取り消せる?

いいえ。マクロを走らせると Excel の取り消しスタックがクリアされるので、マクロが行った ソートは、あなたのコードもユーザーも Ctrl+Z できません。ソートの前に自分で元の並びを 守りましょう — いちばん簡単なのはインデックス列(1, 2, 3…)を刻印してあとでそれに並べ直せる ようにすることか、先にシートをバックアップすることです。

VBA のソートがデータをぐちゃぐちゃにしたのはなぜ?

ほぼ必ず、レコード全体ではなく単一の列をソートしたからです。1 列をソートするとその列だけが 並べ替わり、ほかは元の場所に残るので、各行の値がもう一緒ではなくなります。表全体を — Range("A1").CurrentRegion を — ソートし、ソートキーは並べたい列を指すようにしましょう。

VBA が見出し行をデータの中にソートするのを止めるには?

.SortHeader:=xlYes を渡します(Sort オブジェクトなら .Header = xlYes を設定)。 既定値は xlGuess で、Excel に見出しの検出を試させ、ときに見出しを行の中へソートさせて しまいます。xlYes を明示的に設定すれば、先頭行は見出しとして固定されたままになります。

VBA で Range.Sort と Sort オブジェクトの違いは?

Range.Sort はソートキー 3 つに限られる 1 文のステートメント — 手軽で、ほとんどの仕事に 適しています。Worksheet.Sort オブジェクトは SortFields.Add でキーを 1 つずつ追加し、 無制限のキーと色やアイコンによるソートに対応し、設定をシートに永続させます — なので古いキーを 引き継がないよう、まず SortFields.Clear を呼ばなければなりません。

VBA のソートが 10 を 2 の前に置くのはなぜ?

それらの数値が文字列として保存されていて、文字列は数値順ではなく辞書順("1"、"10"、"2")に 並ぶからです。ソートの前に列を本物の数値に変換しましょう — CStr/Val の変換ガイドを参照 — そうすれば値は本当の数値順に並びます。

検証環境

動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-08。

関連ガイド: VBA Find · VBA AutoFilter · VBA CStr, CDate and Val · VBA Delete Rows · VBA Range