Rails 3.0在每个循环中带有“或”语句

时间:2012-10-31 13:08:29

标签: ruby-on-rails ruby

我正在尝试构建一个比较两个对象的查询,如果它们具有相同的id,则不会获取该记录。我有这个:

@channels.each do |channel|
  unless @guide_channels.where(:id => channel.id).exists?
    @dropdown_channels = @dropdown_channels.where(:id => channel.id)
  end
end

这会创建查询,但会在每个值之间放置一个AND,这不是我想到的。我想要“或”运算符。是否有一个'orwhere'函数我可以使用或者有更好的方法来使用一些比较函数吗?

2 个答案:

答案 0 :(得分:1)

关键是.where()对象的AR::Relation方法将条件添加到一组条件中,然后AND - 在执行查询时一起编写。

您需要做的是像NOT IN这样的查询:

# select all the ids for related @guide_channels
# if @channels comes from a query (so it's a ActiveRecord::Relation):
guide_ids = @guide_channels.where(:id => @channels.pluck(:id)).pluck(:id)
#  use .where(:id => @channels.map {|c| c.id}) instead if @channels is just an array

# then use the list to exclude results from the following.
@dropdown_channels = @dropdown_channels.where("id NOT IN (?)", guide_ids.to_a)

第一个查询将累积@guide_channels中包含条目的频道的所有ID。第二个将使用第一个结果从下拉列表的结果中排除找到的通道。

答案 1 :(得分:0)

这种奇怪的行为是由ActiveRecord对范围的惰性评估引起的。 会发生什么是

@dropdown_channels = @dropdown_channels.where(:id => channel.id)

在您实际使用@dropdown_channels的值之前,不会向数据库发送查询,并且当您将所有条件连接成一个大查询时,这就是您在条件之间得到AND的原因。 / p>

为了强制ActiveRecord急切加载范围,您可以使用all范围或first范围,例如:

@dropdown_channels = @dropdown_channels.where(:id => channel.id).first

这会强制ActiveRecord立即计算返回结果的查询,而不是累积延迟评估的范围。

另一种方法可能是累积所有那些channels_ids,并在稍后的一个查询中获取它们,而不是对每个查询进行查询。这种方法与DB资源相关的成本效益更高。 为了实现这个目标:

dropdown_channels_ids = []
@channels.each do |channel|
  unless @guide_channels.where(:id => channel.id).exists?
    dropdown_channels_ids << channel.id
  end
end
@dropdown_channels = @dropdown_channels.where(:id => dropdown_channels_ids)