カテゴリ: Rails 更新日: 2026/01/18

RailsのNOT NULL制約追加を完全ガイド!既存データへのbackfillと安全な手順

既存データ対応:NOT NULL追加時のbackfill・デフォルト設定の安全手順
既存データ対応:NOT NULL追加時のbackfill・デフォルト設定の安全手順

先生と生徒の会話形式で理解しよう

生徒

「Railsで作っているアプリの項目を、『入力必須』に変えたいんです。でも、すでにデータが入っている場合にエラーが出ちゃって……どうすればいいですか?」

先生

「それは『既存データ』が空っぽ(NULL)のまま、必須というルールを追加しようとしているからですね。データベースが矛盾に困っている状態です。」

生徒

「なるほど!先に中身を埋めなきゃいけないんですね。でも、どうやって安全に作業すればいいんでしょう?」

先生

「『backfill(バックフィル)』という手法を使って、データを埋めてから制約をつける安全なステップがあります。一緒に見ていきましょう!」

1. NOT NULL制約とは?データベースの「空っぽ禁止」ルール

1. NOT NULL制約とは?データベースの「空っぽ禁止」ルール
1. NOT NULL制約とは?データベースの「空っぽ禁止」ルール

Ruby on Rails(ルビーオンレイルズ)の開発で、データベースのカラム(項目の列)に対して、「ここは絶対に空欄(NULL)であってはいけない」というルールを決めることをNOT NULL(ノットヌル)制約と呼びます。

プログラミング未経験の方に分かりやすく例えると、これは「提出書類の必須項目」のようなものです。名前や住所が書いていない書類を受け付けないように、データベースのレベルで「入力漏れ」を絶対に許さない仕組みを作ります。これを設定することで、アプリが予期せぬエラーで止まるのを防ぎ、データの品質を高く保つことができます。

しかし、すでに100件のデータが保存されているテーブルに、後から「この項目は今日から必須です!」とルールを追加しようとすると、既存の100件の中に空欄がある場合、データベースは「ルールを守れていないデータがあるから変更できないよ!」と拒否してしまいます。これがエラーの正体です。

2. 既存データへの対応「backfill」とは?

2. 既存データへの対応「backfill」とは?
2. 既存データへの対応「backfill」とは?

エラーを解決するために必要なのがbackfill(バックフィル)という作業です。これは、過去に遡って(back)、空いている穴を埋める(fill)という意味の専門用語です。

新しいルール(必須設定)を適用する前に、現在空っぽになっている場所に仮のデータや初期値を入れてあげる作業を指します。いわば、書類の必須化を決める前に、過去の書類の空欄に「不明」や「なし」というスタンプを押して回るような作業です。このバックフィルを正しく行わないと、アプリが動いている最中にデータベースの更新が止まってしまう「ダウンタイム」が発生するリスクがあります。

3. 安全な手順ステップ1:デフォルト値を設定する

3. 安全な手順ステップ1:デフォルト値を設定する
3. 安全な手順ステップ1:デフォルト値を設定する

まず最初に行うべき安全な手順は、今後新しく作られるデータが空っぽにならないようにデフォルト値(初期値)を設定することです。マイグレーションファイルを使って、カラムに標準の値を決めてあげます。

例えば、ユーザーの「ランク」という項目を必須にしたいなら、まずは「ランクを指定しなかったら自動的に『一般』にする」という設定を追加します。これだけで、これから入ってくる新しいデータについては「空っぽ問題」が解決します。


class ChangeDefaultToUsers < ActiveRecord::Migration[7.0]
  def change
    # 今後のために、初期値を 'general' に設定する
    change_column_default :users, :rank, from: nil, to: "general"
  end
end

4. 安全な手順ステップ2:既存のNULLを埋める

4. 安全な手順ステップ2:既存のNULLを埋める
4. 安全な手順ステップ2:既存のNULLを埋める

デフォルト値を決めたら、次は過去のデータをお掃除します。これが本当のバックフィル作業です。Railsのマイグレーションの中で直接データを書き換える命令(SQL)を発行します。

パソコンをあまり触ったことがない方でも、このコードのイメージは掴めるはずです。「もし、ランクが空っぽ(NULL)な人がいたら、全員一括で『一般』に書き換えてください」というお願いをデータベースにするのです。データ量が多い場合は、一気にやるとパソコンに負荷がかかるので、少しずつ分ける工夫が必要ですが、まずは基本の書き方を覚えましょう。


class BackfillUserRank < ActiveRecord::Migration[7.0]
  def up
    # 現在空っぽになっているデータをすべて 'general' で埋める
    # update_allは高速にデータを更新する命令です
    User.where(rank: nil).update_all(rank: "general")
  end

  def down
    # 元に戻す必要がある場合の処理(今回は何もしないことが多い)
  end
end

5. 安全な手順ステップ3:ついにNOT NULL制約を追加

5. 安全な手順ステップ3:ついにNOT NULL制約を追加
5. 安全な手順ステップ3:ついにNOT NULL制約を追加

お掃除が完了し、全てのデータに値が入ったことを確認できたら、いよいよ本番のNOT NULL制約を追加します。これで、今後二度とこの項目が空っぽになることはありません。

この段階では、データベースの中にはもう空っぽのデータは一つも存在しないはずなので、エラーが出ることもなくスムーズに設定が完了します。急がば回れ、という言葉通り、この3つのステップを踏むことがシステムを壊さないためのベストプラクティスです。


class AddNotNullToUserRank < ActiveRecord::Migration[7.0]
  def change
    # null: false をつけることで、空っぽを禁止するルールを確定させる
    change_column_null :users, :rank, false
  end
end

6. カラム追加と同時に必須にしたい場合

6. カラム追加と同時に必須にしたい場合
6. カラム追加と同時に必須にしたい場合

もし、これから新しい項目を追加して、最初からそれを「必須」にしたい場合は、もっと簡単です。カラムを作る add_column の命令の中に、最初から null: falsedefault: "値" を両方書いてしまえば良いのです。

これにより、箱が作られた瞬間に初期値が入り、同時に「今後は空っぽ禁止」というルールが適用されます。後から修正するのは大変なので、設計の段階で「ここは必須だな」と分かっている場合は、最初からこのセットで書く癖をつけておくと、後のバックフィル作業が不要になります。


class AddPointsToUsers < ActiveRecord::Migration[7.0]
  def change
    # カラム追加時に、初期値0、かつ空っぽ禁止を同時に設定する
    add_column :users, :points, :integer, default: 0, null: false
  end
end

7. バリデーション(Model)との違いに注意

7. バリデーション(Model)との違いに注意
7. バリデーション(Model)との違いに注意

Railsには validates :name, presence: true という、アプリ側でのチェック機能(バリデーション)もあります。しかし、これだけでは不十分な場合があります。

バリデーションは、アプリの画面から入力されたときには効きますが、直接データベースを操作したり、他のプログラムからデータを流し込んだりしたときにはスルーされてしまうことがあるからです。今回学んだデータベース側のNOT NULL制約は、いわば「物理的な壁」です。どんなルートから来ても、空っぽのデータを通さない最強の守りになります。両方を組み合わせて使うのが、安全なスキーマ設計の極意です。

8. 大規模なデータがある時の「小分け」のコツ

8. 大規模なデータがある時の「小分け」のコツ
8. 大規模なデータがある時の「小分け」のコツ

もし、あなたのアプリに100万件以上の膨大なデータがある場合、update_all を使うとデータベースが長時間ロック(他の人が使えない状態)されてしまうことがあります。その場合は、一度に全部やるのではなく、1000件ずつに分けてお掃除するなどの工夫をします。

プロの世界では、こうした「止まらないシステム作り」のために、バックフィルを数日かけて少しずつ行うこともあります。初心者のうちは、まずは「データを埋めてから制約をつける」という順番をしっかり守ることから始めましょう。この慎重さが、ユーザーの大切なデータを守ることに繋がります。

カテゴリの一覧へ
新着記事
New1
Rails
RailsのNOT NULL制約追加を完全ガイド!既存データへのbackfillと安全な手順
New2
データベース
SQLインデックスとは?初心者でもわかるデータベース高速化の仕組みと設定方法
New3
Ruby
Rubyハッシュの基本!キーの追加・削除・変更を初心者向けに徹底解説
New4
Rails
Railsのreferences/belongs_toを完全ガイド!外部キーとインデックスの作り方
人気記事
No.1
Java&Spring記事人気No1
Ruby
PATHと環境変数の正しい設定!Windows・Mac・Linux別チェックリスト付き
No.2
Java&Spring記事人気No2
Ruby
Rubyのハッシュを徹底比較!シンボルキーと文字列キーの違いと使い分け
No.3
Java&Spring記事人気No3
データベース
ACID特性とは?データベーストランザクションの信頼性を初心者向けに徹底解説
No.4
Java&Spring記事人気No4
Rails
Railsマイグレーションの型選びを完全ガイド!初心者が迷わないカラム設計
No.5
Java&Spring記事人気No5
Rails
Railsで日本語と時刻の設定をしよう!初心者でも安心のlocale/zone初期設定チートシート
No.6
Java&Spring記事人気No6
Ruby
Rubyのハッシュ走査を完全マスター!each・each_key・each_valueの使い方
No.7
Java&Spring記事人気No7
Rails
Railsインデックス設計の極意!爆速サイトを作るためのスキーマ設計ガイド
No.8
Java&Spring記事人気No8
データベース
SQLのCOMMITとROLLBACKとは?トランザクション操作を初心者向けに完全解説