如何确保不通过activerecords将重复的行添加到我的数据库表中?

所以我有一张叫做投票的桌子。

有三列。 VoterID,VoteforID和投票。

每个单独列的唯一性并不重要,但是同一行不能重复两次

例如。 这是对的

Voter_id | Votefor_id | 投票

1 | 2 | 1

1 | 3 | 1

1 | 4 | 1

2 | 1 | 2

这是错的。

Voter_id | Votefor_id | 投票

1 | 2 | 1

1 | 2 | 1

1 | 4 | 1

2 | 1 | 2

目前,我在添加记录之前正在执行select语句,以确保不存在相同的行。 当然,有一个更好的解决方案。

谢谢。

如果数据完整性至关重要,则不应使用validation来保证唯一性。 它可能会失败。 保证唯一性的唯一方法是使用数据库约束。 这是因为Rails validates_uniqueness可能具有竞争条件。

创建迁移以添加索引,或更改现有索引以反映此更改:

对于新表:

 class CreateVotes < ActiveRecord::Migration def change create_table :votes do |t| t.belongs_to :voter t.belongs_to :votefor t.string :vote # Choose the correct column type t.timestamps end add_index :votes, [:voter_id, :votefor_id, :vote], unique: true end end 

对于现有表格:

 class AddUniqueIndexToVotes < ActiveRecord::Migration def change add_index :votes, [voter_id, votefor_id, vote], unique: true end end 

现在,如果您想向用户提供他们已经投票的反馈,您可以按照其他人的建议继续添加validation:

 validates :voter_id, uniqueness: { scope: [:votefor_id, :vote] } 

scope上使用scope参数,例如:

 validates :voter_id, uniqueness: { scope: :votefor_id } 

范围参数可以是多个:

 validates :voter_id, uniqueness: { scope: [:votefor_id, :vote] } 

更新:(应用validation与Db约束)

这实际上取决于细节,但对于手头的情况,我个人认为db约束在这里有点过分,因为竞争条件( 在rails唯一性validation中记录 )发生的可能性很低,因为我们需要

两个记录具有完全相同的voter_id,votefor_id和投票在同一时间提交(我们正在谈论毫秒),如果我可以假设一些OP业务逻辑, voter通常会登录用户意味着重复的条目应该只来自他!

结论:

我有很多使用uniqueness_validation在生产中运行的rails应用程序就好了,只有一次它在一个具有许多同时条目的单个列的唯一性检查情况下失败了(显然我需要db约束)。

如果数据完整性至关重要,您应该确保使用db约束。

或者,如果您不介意开销,特别是如果您可以从(voter_id,votefor_id和投票)的新索引中获益以提高搜索速度,我会说使用db约束。

否则我会说:保持简单并保持uniquess_valdation

在表定义中,添加UNIQUE表约束:

 CREATE TABLE votes ( voter_id int NOT NULL, votefor_id int NOT NULL, vote int NOT NULL, UNIQUE (voter_id, votefor_id, vote) ); 

这样PostgreSQL会检查重复项。 如果您尝试INSERT一条记录,其值对于所有三列都与现有记录中的值相同,则会引发错误。