列に一意のインデックスを指定したいが、NULL
値も許可する必要があります(複数のレコードにNULL
値を含めることができます)。 PostgreSQLでテストすると、NULL
値を持つレコードが1つあることがわかりますが、次の場合は問題が発生します。
irb(main):001:0> u=User.find(5)
User Load (111.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 5]]
=> #<User id: 5, email: "[email protected]", created_at: "2013-08-28 09:55:28", updated_at: "2013-08-28 09:55:28">
irb(main):002:0> u.email=nil
=> nil
irb(main):003:0> u.save
(1.1ms) BEGIN
User Exists (4.8ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" IS NULL AND "users"."id" != 5) LIMIT 1
(1.5ms) ROLLBACK
=> false
したがって、データベースで許可されている場合でも、Railsは最初にUser
が存在するかどうかを確認し、email
列をNULL
。データベースが許可するだけでなく、Railsも上記のように最初にチェックしない方法はありますか?
ユーザーがメールを入力する必要はありませんが、入力する場合はメールでユーザーを検索できるようにする必要があります。ユーザーをメールに関連付ける別のモデルを作成できることはわかっていますが、私は上記の方法でそれを実行したいのです。
[〜#〜] update [〜#〜]:email
列:
class AddEmailToUsers < ActiveRecord::Migration
def change
add_column :users, :email, :string
add_index :users, :email, :unique => true
end
end
そして、これがUser
モデルに追加したコードです。
validates :email, uniqueness: true
validates
モデルにUser
呼び出しを追加したことを忘れていました。 Railsが最初にチェックすることは理にかなっています。データベースの他の質問が一意のインデックスとNULL
フィールドを持つことが安全であるかどうかです)? Railsで指定します。メールがnil
でない限り、一意であることを検証したいですか?
移行は機能し、複数のnull
値を許可します(ほとんどのデータベースエンジンの場合)。
ただし、ユーザークラスの検証は次のようになります。
validates :email, uniqueness: true, allow_nil: true
これがデータベースレベルで機能する理由を明確にするには、SQLで使用される3つの値のロジックを理解する必要があります:true
、false
、null
。
null
は、通常、不明を意味するものと解釈されます。したがって、操作におけるその意味論は、通常、その特定の値が何であるかを知らず、回答がまだ解決できるかどうかを確認することと同じです。たとえば、1.0 * null
はnull
ですが、null OR true
はtrue
です。前者の場合、unknownによる乗算は不明ですが、後者の場合、条件式の後半によりステートメント全体が常に真になるため、左側が何であっても問題ありません。
インデックスに関しては、標準では何も指定されていないため、ベンダーは未知の意味を解釈する必要があります。個人的には、PostgreSQLのドキュメントのように一意のインデックスを定義する必要があると思います。
インデックスが一意であると宣言されている場合、インデックス値が等しい複数のテーブル行は許可されません
質問はnull = null
の値が何であるかです。正解はnull
です。したがって、これらのPostgreSQLドキュメントの行間を少し読んで、一意のインデックスによって、その値に対して等値演算子がtrueを返す複数の行が許可されないと言う場合、複数のnull
値を許可する必要があります。これがまさにPostgreSQLの仕組みです。そのため、その設定では、null
を値として持つ複数の行を持つ一意の列を作成できます。
一方、一意のインデックスの定義を解釈して、不等式演算子がfalseを返さない複数の行を許可しないようにしたい場合、null
値を持つ複数の行を持つことはできません。誰がこの反対のセットアップで動作することを選択するでしょうか?これは、Microsoft SQL Serverが一意のインデックスを定義するために選択する方法です。
一意のインデックスを定義するこれらの両方の方法は、2003 SQL標準のnull
の定義に基づいて正しいです。したがって、それは実際に基盤となるデータベースに依存します。しかし、そうは言っても、大半はPostgreSQLと同様に動作すると思います。