4

我正在尝试构建一个比较两个对象的查询,如果它们相同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”功能,或者是否有更好的方法可以通过一些比较功能来做到这一点?

4

2 回答 2

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)

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

于 2012-10-31T14:49:38.820 回答
0

这种奇怪的行为是由于 ActiveRecord 对范围的惰性评估。发生的事情是这条线

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

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

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

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

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

另一种方法可能是累积所有这些 channels_id 并稍后在一个查询中获取它们,而不是为每个查询。这种方法与数据库资源相关的成本效益更高。为了实现这一点:

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)
于 2012-10-31T14:44:03.047 回答