注文管理やタスク管理のExcelで、「H~AJ列のどこかに x(または 1)を入れたら、その日の注文日をG列に自動入力して、翌日や再計算でも絶対に変わってほしくない」というニーズはとても多いです。しかし、普通に TODAY() 関数を使うと、ブックを開き直したり再計算するたびに日付が更新されてしまいます。本記事では、関数だけでできる方法からVBAを使った完全自動化まで、「一度入った日付を固定する」ための3つの実践的な方法を、非エクセルユーザーでも迷わないレベルまで丁寧に解説します。
やりたいことの整理:どのセルにどんな日付を入れたいか
まずは要件をハッキリさせます。この記事で想定しているシート構成と要望は次のとおりです。
| 列 | 役割 | 例 |
|---|---|---|
| G列 | 注文日を記録する列 | G5, G6, G7 … |
| H~AJ列 | ステータスやチェック用の列 | H5:AJ5 に「x」や「1」が入る |
要件は次の2パターンがあります。
- パターンA:行ごとにG列へ注文日を記録したい
同じ行の H~AJ 列のどこかに「x」または「1」が入ったタイミングで、その行の G セル(例:G5)にその日の日付を自動で入れたい。 - パターンB:シート全体で最初に注文が入った日をG4に記録したい
シートのどこかの H~AJ 列に初めて「x/1」が入力された瞬間に、G4 にその日付を記録し、その後は変えたくない。
さらに、G列に入った日付を「トリガー(x/1)が消えたら消すのか」「一度入ったら残すのか」も運用により変わります。この記事ではこの違いも含めて整理していきます。
なぜTODAY関数だけでは日付を固定できないのか
よくある最初の試みが、次のような式です。
=IF(COUNTIF(H5:AJ5,"x")>0, TODAY(), "")
一見正しそうですが、この式には大きな問題があります。
TODAY()は「その日の日付」を返す揮発性関数であり、再計算やファイルの再オープンのたびに評価し直される。- つまり「昨日xを入れてG5に昨日の日付が入った」状態で翌日にブックを開くと、G5は「今日の日付」に更新されてしまう。
このため、「自動で入れて、二度と変わらない」という要件は、通常の関数だけでは原理的にかなり難しい問題になります。この記事では次の3つの戦略でこの問題に立ち向かいます。
| 方法 | 特徴 | 自動化レベル | おすすめ度 |
|---|---|---|---|
| 方法1:手動入力(ショートカット) | 一番簡単。確実に日付が固定される。 | 半自動 | ◎(誰でも安全) |
| 方法2:関数+反復計算(循環参照) | 式だけで「ほぼ固定」を実現。 | 自動(Excel設定が必要) | ○(ルール徹底が必要) |
| 方法3:VBA(Worksheet_Change) | 完全自動&完全固定。デスクトップ版限定。 | 完全自動 | ◎(Excelデスクトップ前提なら最強) |
方法1:ショートカットで今日の日付を「固定値」として入力する
最もトラブルが少なく、非エクセルユーザーにも説明しやすいのが、この「ショートカットで固定値として入力する」方法です。自動ではありませんが、ほぼワンタッチなので現場運用では十分実用的です。
よく使う日付・時刻ショートカット
| 入力したい内容 | ショートカット | 説明 |
|---|---|---|
| 今日の日付(固定) | Ctrl + ; | システム日付が固定値として入る。再計算しても変わらない。 |
| 現在時刻(固定) | Ctrl + Shift + ; | 時刻も一緒に残したい場合に使用。 |
| 日付+時刻(固定) | Ctrl + ; → スペース → Ctrl + Shift + ; | 一つのセルに日時を残したいとき。 |
G列に手動で日付を刻印する運用例
- H~AJ列のどこかに「x」または「1」を入力する。
- 同じ行のG列(例:G5)を選択する。
Ctrl + ;を押す。
これだけで、G列には固定値として日付が入ります。以後どれだけ再計算しても、ファイルを再起動しても、値は一切変わりません。
この方法のメリット・デメリット
| メリット | デメリット | |
|---|---|---|
| ◎ | 再計算・関数・VBAなどの仕組みに依存しない。Excelさえ動けば確実。 | 完全自動ではない(人がショートカットを押す必要がある)。 |
| ◎ | マクロ禁止の環境、Excel Online などどこでも使える。 | 入力者がショートカットを知らないと使われない可能性。 |
「入力者がExcelに詳しくない」「社内でマクロ禁止」「数式の設定を複雑にしたくない」といった場合は、この方法が最もおすすめです。
方法2:関数だけで“ほぼ固定”にする(反復計算・循環参照)
「どうしても関数だけでやりたい」「マクロは使えない」という場合は、Excelの反復計算(循環参照)の機能を使います。
アイデアはシンプルです。
- セル自身(例:G5)を式の中で参照し、一度入れた日付を自分自身から読み出すことで更新を止める。
- これには「セルが自分自身を参照しても計算を止めない」設定(反復計算)が必要。
事前準備:反復計算(循環参照)を有効にする
Excelで反復計算を有効にする手順は次のとおりです。
- Excelのメニューから
[ファイル] → [オプション] をクリック。 - 左側のメニューから [数式] を選択。
- 「反復計算を有効にする」 にチェックを入れる。
- 「最大反復回数」:1 に設定。
- 「最大変更」:0.001(初期値のままでOK)。
- [OK] を押して設定を保存。
この設定を行うと、セルが自分自身を参照していても1回だけ再計算して落ち着くようになります。これを利用し、「Gセルが空のときだけTODAY()を使い、それ以外はGセルの値をそのまま返す」という式を作ることで、日付を実質的にロックします。
パターンA-1:行ごとのG列に注文日を刻印し、トリガーが消えたら日付も消す
まずは、「行内に x / 1 がある間だけ日付を保持し、すべて消えたら日付も消す」バージョンです。
G5セルに次の式を入力します(5行目として説明しますが、実際には必要な行に合わせて変更してください)。
=IF(COUNTIF($H5:$AJ5,"x")+COUNTIF($H5:$AJ5,"1")>0,
IF(G5="",TODAY(),G5),
""
)
この式の動きは次の通りです。
COUNTIF($H5:$AJ5,"x")+COUNTIF($H5:$AJ5,"1")>0
→ 行内(H5:AJ5)に「x」か「1」が1つでもあればTRUE。- トリガーがあるとき(TRUEのとき):
- もしG5が空欄なら:
TODAY()を実行して今日の日付をセット。 - もしG5に既に日付が入っていれば:そのまま
G5の値を返す(更新しない)。
- もしG5が空欄なら:
- トリガーがないとき(FALSEのとき):
→""(空文字)を返し、G5を空欄にする。
この式をG列の必要な行にコピーすれば、行ごとに「x/1ありの間は日付が保持され、消したら日付も消える」動きになります。
パターンA-2:行ごとのG列に一度だけ注文日を刻印し、その後は消さない
多くの業務では、「一度注文が入ったという事実」はトリガーが消えても残したいはずです。その場合は次の式を使います。
=IF(G5<>"",G5,
IF(COUNTIF($H5:$AJ5,"x")+COUNTIF($H5:$AJ5,"1")>0, TODAY(), "")
)
この式は、先ほどとは判定の順番が逆になっています。
- まず
IF(G5<>"",G5, ...)で
- G5に何か入っていれば → そのままG5を返す(以後は絶対に変えない)。
- G5が空なら → 次のIFの判定に進む。
COUNTIF(...)でH5:AJ5に x / 1 があるかチェックし、あればTODAY()をセット。- 一度G5に日付が入ると、1の条件で常にG5が優先されるため、トリガーが消えても日付は消えない。
「注文が一度でも入った行は、その事実をずっと残したい」という場合には、こちらの式が向いています。
パターンB:シート全体の最初の注文日をG4に刻印する
注文管理表全体で、「最初にどこかの行で x / 1 が入力された日」を記録したい場合は、G4に次の式を入れます。
=IF(G4<>"",G4,
IF(COUNTIF($H:$AJ,"x")+COUNTIF($H:$AJ,"1")>0, TODAY(), "")
)
この式のポイントは次の通りです。
- 最初に
G4<>""をチェックし、すでに値があれば常にそれを返す。 - まだ空欄なら、シート全体(H:AJ列)で x / 1 が一つでもあるかをチェック。
- 初めてx/1が入力されたタイミングでTODAY()が実行され、その値がG4に固定される。
- 以後はG4が空ではなくなるため、TODAY()は二度と実行されない。
反復計算方式の注意点
この方法には、いくつか重要な注意点があります。
- ブックごとに「反復計算を有効にする」設定が必要です。
- 設定を知らない人が「循環参照があるからおかしい」と判断して設定を戻すと、意図しない挙動になります。
- 大規模なブックで多用すると、計算が重くなる場合があります。
そのため、チームで運用する場合は、次のようなルールをシートに明記しておくと安心です。
- 「このブックは、反復計算(循環参照)を使用しています。設定を変更しないでください。」とメモを残す。
- 設定方法や式の意味を簡単に記した説明シートを1枚用意しておく。
方法3:VBAで完全自動化(Worksheet_Changeイベント)
Excelのデスクトップ版が使えて、マクロが許可されている環境なら、VBAによる自動化が最も確実です。Worksheet_Change イベントを使うことで、H~AJ列に値が入力された瞬間に、G列へ「値として」日付を書き込むことができます。
VBAでできること・できないこと
- できること
- H~AJ列の変更を監視し、条件を満たした行のG列に日付を自動入力。
- 日付を「関数」ではなく固定値として書き込むため、再計算しても絶対に変わらない。
- できない/制約があること
- Excel for Web やスマホ版のExcelではVBAは動かない。
- マクロが禁止されている環境では使えない。
事前準備:マクロ有効ブックとして保存する
- 対象のExcelファイルを開く。
- 「名前を付けて保存」で、ファイルの種類を
「Excel マクロ有効ブック(*.xlsm)」 に変更して保存。 - 次回からこのファイルを開いたときに、「マクロを有効にしますか?」と聞かれたら [有効にする] を選択。
パターンA:行ごとにG列へ注文日を刻印する標準コード
次のコードは、「H5:AJ列のどこかにx/1が入力されたら、その行のG列に今日の日付を自動で刻印」するものです。
- シート下部のタブを右クリックして、[コードの表示] を選択。
- 表示されたVBAエディタのウィンドウに、次のコードを貼り付ける。
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngMon As Range, rngHit As Range, c As Range
On Error GoTo CleanExit
' 監視範囲:H5:AJ(開始行は必要に応じて変更)
Set rngMon = Intersect(Me.UsedRange, Me.Range("H5:AJ1048576"))
If rngMon Is Nothing Then GoTo CleanExit
Set rngHit = Intersect(Target, rngMon)
If rngHit Is Nothing Then GoTo CleanExit
Application.EnableEvents = False
For Each c In rngHit.Cells
Dim r As Long
Dim dateCell As Range
Dim hasTrig As Boolean
r = c.Row
Set dateCell = Me.Cells(r, "G")
' 行内にトリガー(x/1)があるか判定
hasTrig = (Application.CountIf(Me.Range("H" & r & ":AJ" & r), "x") > 0) _
Or (Application.CountIf(Me.Range("H" & r & ":AJ" & r), "1") > 0)
' G列が空欄かつトリガーあり → 今日の日付を固定値で刻印
If dateCell.Value = "" And hasTrig Then
dateCell.Value = Date
' 表示形式を指定したい場合(例:yyyy/mm/dd)
' dateCell.NumberFormat = "yyyy/mm/dd"
End If
' 【任意】トリガーが無くなったら日付も消したい場合は下行のコメントを外す
'If Not hasTrig And dateCell.Value <> "" Then dateCell.ClearContents
Next c
CleanExit:
Application.EnableEvents = True
End Sub
このコードのポイントは次の通りです。
- H5:AJ列の範囲内で変更があったセルのみを対象に処理するため、無駄な処理を抑えている。
- 条件を満たしたとき、G列には Date(今日の日付)が値として書き込まれる。
- 一度入った日付は、
dateCell.Value = "" And hasTrigの条件を満たさない限り書き換えない。 - 「トリガーが無くなったら日付も消したい」場合は、コメントアウトされている行のコメントを外すだけで対応できる。
パターンB:シート全体の最初の注文日をG4に一度だけ刻印するコード
次のコードは、「シート内のH~AJに初めてx/1が入力されたタイミングでG4に日付を刻印し、その後は変えない」というものです。
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngMon As Range
On Error GoTo CleanExit
Set rngMon = Intersect(Me.UsedRange, Me.Range("H:AJ"))
If rngMon Is Nothing Then GoTo CleanExit
If Intersect(Target, rngMon) Is Nothing Then GoTo CleanExit
Application.EnableEvents = False
' まだG4が空欄で、シート内どこかにx/1が入力されたら日付を刻印
If Me.Range("G4").Value = "" Then
If (Application.CountIf(rngMon, "x") + Application.CountIf(rngMon, "1")) > 0 Then
Me.Range("G4").Value = Date
'Me.Range("G4").NumberFormat = "yyyy/mm/dd"
End If
End If
' 【任意】シート内からトリガーが全て消えたらG4を空欄に戻したい場合
'If (Application.CountIf(rngMon, "x") + Application.CountIf(rngMon, "1")) = 0 Then
' Me.Range("G4").ClearContents
'End If
CleanExit:
Application.EnableEvents = True
End Sub
このコードでは、G4に一度でも日付が入ると以後は変更されないため、ブック全体の「最初の注文日」の刻印として使えます。
VBAが動かないときのチェックポイント
「コードを書いたのに動かない…」というときは、次の点を確認してみてください。
| チェック項目 | 確認すべき内容 |
|---|---|
| コードの場所 | コードは標準モジュールではなく、対象シートのコードモジュールに入っていますか? (シート名を右クリック → [コードの表示] で開く場所です) |
| マクロの有効化 | ファイルを開いたときに「マクロを有効にする」を選択しましたか? 保護ビューでは動作しません。 |
| イベント無効化 | 過去のエラーで Application.EnableEvents = False のままになっていませんか?VBEの即時ウィンドウで Application.EnableEvents = True と打ってEnterすると元に戻せます。 |
| 監視範囲 | H5:AJ1048576 の範囲が、実際の表の開始行と合っていますか? 始まりの行を変える場合はコード内のセル番地を調整してください。 |
| Excelの種類 | Excel for Web やスマホアプリではVBAは動きません。デスクトップ版のExcelでテストしましょう。 |
どの方法を選ぶべきか:状況別のおすすめ
ここまで紹介した3つの方法を、想定シーン別に整理します。
| 状況 | おすすめの方法 | 理由 |
|---|---|---|
| Excel初心者も使う、マクロ禁止の環境 | 方法1:ショートカットで固定値入力 | トラブルになりにくく、説明も簡単。「G列を選んで Ctrl+; を押すだけ」と教えれば十分。 |
| マクロ禁止だが、担当者はExcelにそこそこ慣れている | 方法2:反復計算+自参照のIF式 | 式だけで自動刻印できる。反復計算の仕組みを理解し、設定を変えないよう運用できるなら有力な選択肢。 |
| Excelデスクトップ版があり、マクロが使える | 方法3:VBA(Worksheet_Change) | 入力者は「x」や「1」を打つだけでよく、日付は完全自動で固定される。もっとも運用負担が小さい。 |
| 日付の正確性が絶対に重要(法的な記録など) | 方法1 または 方法3 | 関数での擬似固定ではなく、「値」として書き込まれる方法が安心。VBAが使えないなら手動ショートカット一択。 |
よくあるエラー:セルに「FALSE」と表示される原因と対策
質問の中で「式を別の式と合体させたら FALSE が出力される」という話がありました。これは、IF関数の偽の場合の戻り値が明示されていないか、意図せず論理値FALSEそのものを返してしまっていることが原因です。
たとえば次のような式を考えてみます。
=IF(COUNTIF($H5:$AJ5,"x")>0, IF(G5="", TODAY(), G5))
この式には「偽の場合(xが1つもない場合)の戻り値」がありません。その場合、Excelは内部的に FALSE を返し、それがセルに表示されることがあります。
これを避けるには、必ずIFの第3引数(偽の場合)に、空文字""か、表示させたい値を指定します。
=IF(COUNTIF($H5:$AJ5,"x")>0, IF(G5="", TODAY(), G5), "")
さらに、x以外に「1」もトリガーにしたい、G5の値を優先して固定したい、という今回の要件を組み込んだ正しい形が、先ほど紹介した次の式です。
=IF(COUNTIF($H5:$AJ5,"x")+COUNTIF($H5:$AJ5,"1")>0,
IF(G5="", TODAY(), G5),
""
)
または、「一度入れた日付はトリガーが消えても残す」バージョンならこちらです。
=IF(G5<>"",G5,
IF(COUNTIF($H5:$AJ5,"x")+COUNTIF($H5:$AJ5,"1")>0, TODAY(), "")
)
いずれにしても、IFをネストして複雑にしていくときは、必ず
- 真の場合に返す値
- 偽の場合に返す値
の両方を入れるようにしておくと、「FALSEが出てしまう」トラブルを防ぐことができます。
非エクセルユーザーでも迷わない運用にするための工夫
式やVBAを凝っても、現場で入力する人が混乱してしまっては意味がありません。特に、「普段Excelを使い慣れていない人が入力だけ担当する」というケースでは、次のような工夫をしておくと運用が安定します。
1. 入力ルールをシート上に明記する
- シート上の目立つ場所に、「H~AJ列に x または 1 を入力すると、G列に注文日が自動で入ります」と書いておく。
- ショートカット運用の場合は、「G列を選択して Ctrl+; を押すと今日の日付が入ります」と具体的に書く。
2. 数式や日付セルを保護する
G列に式を入れている場合、誤って式を消されてしまうと機能しなくなります。次のような手順でセル保護をしておくと安心です。
- G列全体を選択し、右クリック → [セルの書式設定] → [保護] タブ → 「ロック」にチェックを入れる。
- H~AJ列は、セルのロックを外しておく。(既定ではロックされているので、ロックのチェックを外す)
- [校閲] タブ → [シートの保護] を有効にし、「ロックされたセルの選択」だけを制限するなど、適切なオプションを設定。
これで、入力担当者はH~AJ列にだけ触れるようになり、G列の数式や日付が誤って消されるリスクを減らせます。
3. バックアップを取り、テスト用シートで検証してから本番へ
特に反復計算やVBAを使う場合は、いきなり本番ファイルに組み込むのではなく、
- ファイルをコピーしてテスト用のブックを作る。
- 少数のダミーデータで、期待通りの日付が入るかを確認する。
- 問題がなければ、本番ファイルに組み込む。
という手順を踏んでおくと、安全に導入できます。
まとめ:Excelで「xを入れたら日付を自動入力+固定する」ための考え方
Excelで「H~AJ列に x(または 1)を入れたら、G列にその日の注文日を自動で入れたい。しかも再計算しても変わらないようにしたい」という要望は、一見シンプルでも、TODAY()が再計算で更新されるという性質のために意外と奥が深いテーマです。
本記事で紹介したように、実現方法は大きく3つに分かれます。
- 方法1:Ctrl+; で日付を固定値として手動入力
→ 最も堅牢で環境を選ばない。非エクセルユーザーにも説明しやすい。 - 方法2:反復計算(循環参照)+自参照IFで「ほぼ固定」にする
→ 関数だけで自動化できるが、反復計算設定や運用ルールの徹底が必須。 - 方法3:VBA(Worksheet_Change)で完全自動化
→ 入力者は x / 1 を入れるだけでよく、G列の日付は固定値として自動刻印される。Excelデスクトップ版&マクロ有効が前提。
どの方法にも一長一短がありますが、「日付が勝手に変わってしまう」というトラブルを避けるためには、日付を「数式の結果」ではなく「値」として扱うことが重要です。
まずは、ご自身の環境(マクロの可否、入力者のスキル、システム要件)を踏まえて、
- シンプルに運用したいなら → ショートカット
- どうしても関数だけでやりたいなら → 反復計算+自参照IF
- デスクトップ版でがっつり自動化したいなら → VBA
という基準で選んでもらえれば、Excelでの注文日管理が格段に使いやすくなるはずです。

コメント