yamarkz's blog

紫陽花

Rails 複数のカラムに 一意制約 (ユニーク)を設ける

Rails ActiveRecordで複数のカラムに対して一意制約を設ける。複合一意制約

複数のカラムの組み合わせがユニークであってほしい場面に遭遇しました。

例えば、Siteテーブル Keywordテーブルが存在する時、

Keywordテーブルにある site_id と name そして date の組み合わせはユニークでなければいけない状況でした。

site_id, name, date の組み合わせがユニー

この組み合わせのユニークを担保するために、Railsアプリケーション側でユニークであることを確認するvalidate処理を設けて解決させました。

class Keyword < ActiveRecord::Base
  validates :site_id,  uniquness: { scope: [:name, :date]  }
end

上記の様に書くことで、データのinsertを行う際にvalidationが走り、値がユニークであるかどうかを確認するようになります。

既に組み合わせが存在していた場合にはデータのinsert処理をRollBackされます。

scopeの配列に値を追加すればさらに複数のカラムの制約を追加することができます。

もう一つ、一意制約の状態を担保する手段としてDB側にunique indexを追加する方法があります。

class CreateKeyword < ActiveRecord::Migration
  def  change
    create_table  :keywords do |t|
        t.integer  :site_id,  null: false
        t.string    :name,    null: false
        t.date      :date,    null:  false
        t.timestamp
    end
    add_index  :keywords, [:site_id, :name, :date], unique: true
  end
end

add_indexで site_id, name, date の3つのカラムにuniqueであることを追加しています。
上記でデータをInsetする際にDB側で処理が制限されるようになります。

以上のようにDBに保存するデータのユニーク状態を担保する方法は、

1. アプリケーション側でvalidateを設けて制御する
2. DB側でカラムに対して制約を設けて制御する

という2つの方法が存在します。

方法としては2つ存在しますが、個人的には2つとも制約を付けるべきだと考えています。

理由としてはアプリケーションレイヤーでのエラーなのか、DBレイヤーでのエラーなのかをはっきりさせることで未然にバグや、処理の抜け漏れに対応することができるからです。

制約を設けなかったが故に不正な値が存在してしまい、その修正対応で追われて苦労しないためにも、DBの制約はしっかり定義しましょう。