记录不存在则创建

问题描述 投票:0回答:5

我的 Rails 应用程序中有 3 个模型

class Contact < ActiveRecord::Base
  belongs_to :survey, counter_cache: :contact_count
  belongs_to :voter
  has_many :contact_attempts
end

class Survey < ActiveRecord::Base
  has_many :questions
  has_many :contacts
end

class Voter < ActiveRecord::Base
  has_many :contacts
end

联系人由 voter_id 和 Survey_id 组成。我的应用程序的逻辑是,在任何给定的调查中,选民只能有一个联系人。

现在我正在使用以下代码来强制执行此逻辑。我在联系人表中查询与给定 voter_id 和 Survey_id 匹配的记录。如果不存在则创建它。否则它什么也不做。

if !Contact.exists?(:survey_id => survey, :voter_id => voter)
   c = Contact.new
   c.survey_id = survey
   c.voter_id = voter
   c.save
end

显然这需要一个选择和一个插入查询来创建 1 个潜在联系人。当我一次添加可能数千个联系人时。

现在我正在使用 Resque 来允许它在后台运行并且远离 ui 线程。我可以做些什么来加快速度并提高效率?

ruby-on-rails ruby-on-rails-4
5个回答
53
投票

您可以执行以下操作:

Contact.where(survey_id: survey,voter_id: voter).first_or_create

24
投票

您应该首先添加一个数据库索引,以尽可能将这种情况强制在最低级别:

add_index :contacts, [:voter_id, :survey_id], unique: true

然后您应该在 ActiveRecord 级别添加唯一性验证:

validates_uniqueness_of :voter_id, scope: [:survey_id]

如果指定选民和调查存在联系人,

contact.save
将返回
false

更新:如果您创建索引,那么唯一性验证将运行得非常快。


20
投票

看看这些链接是否可以帮助您。

这些链接适用于 Rails 4.0.2,但您可以在 api 码头中进行更改

来自 apidock:first_or_createfind_or_create_by
来自 Rails 指南:查找或创建者


4
投票

如果让MySQL来处理会更好。

创建迁移并将复合唯一键添加到

survey_id, voter_id

add_index :contact, [:survey_id, :voter_id], :unique=> true

现在

Contact.create(:survey_id=>survey, :voter_id=>voter_id) 

仅当没有重复项时才会创建新记录。


0
投票

您可以使用

find_or_create_by
方法来实现此目的。如果您只想检查某些字段,则可以选择将块传递给此方法。仅当没有找到记录时才会执行此块。

文档说

只有在创建客户时才会执行该块。我们第二次运行此代码时,该块将被忽略。

它的工作原理如下:

Contact.find_or_create_by({survey_id: survey,voter_id: voter})

或者像这样的块:

Contact.find_or_create_by({survey_id: survey}) do |contact|
    contact.voter_id = voter
end
© www.soinside.com 2019 - 2024. All rights reserved.