ActiveRecord选择OR和独占限制

时间:2018-04-06 14:48:57

标签: ruby-on-rails activerecord

我需要查询数据库并检索最近10个活动或拒绝的对象。我们使用以下内容:

User.where(status: [:active, :declined]).limit(10)

现在我们需要获得每个状态的最后10个(总共20个用户)

我尝试了以下内容:

User.where(status: :active).limit(10).or(User.where(status: : declined).limit(10))
# SELECT  "users".* FROM "users" WHERE ("users"."status" = $1 OR "users"."status" = $2) LIMIT $3

这与上一个查询相同,只返回10个混合状态的用户。

如何使用单个查询获取最近10位活跃用户和最后10位拒绝用户?

4 个答案:

答案 0 :(得分:1)

我不确定SQL是否允许执行您想要的操作。我会尝试的第一件事是使用子查询,如下所示:

class User < ApplicationRecord
  scope :active, -> { where status: :active }
  scope :declined, -> { where status: :declined }
  scope :last_active_or_declined, -> {
    where(id: active.limit(10).pluck(:id))
      .or(where(id: declined.limit(10).pluck(:id))
  }
end

然后你可以做的其他地方

User.last_active_or_declined()

这样做是为了执行2个不同的子查询,分别询问每个用户组,然后获取propper组ID中的那些用户。我会说你甚至可以忘记pluck(:id)部分,因为ActiveRecord非常聪明,可以为select添加正确的SQL条款,但我不是百分百肯定而且我不喜欢我手头有任何Rails项目,我可以尝试这个。

答案 1 :(得分:0)

limit不是#or关系的允许值。如果您检查Rails代码,则引发的错误来自here

def or!(other) # :nodoc:
  incompatible_values = structurally_incompatible_values_for_or(other)

  unless incompatible_values.empty?
    raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
  end
  # more code
end

您可以在代码here中检查哪些方法受到进一步限制:

STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
def structurally_incompatible_values_for_or(other)
  STRUCTURAL_OR_METHODS.reject do |method|
    get_value(method) == other.get_value(method)
  end
end

您可以在Relation课程here中看到限制受限:

SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
                        :reverse_order, :distinct, :create_with, :skip_query_cache,
                        :skip_preloading]

所以你不得不诉诸原始SQL我害怕

答案 2 :(得分:0)

我认为您无法使用单个查询来执行此操作,但您可以使用两个查询来执行此操作,获取记录ID,然后使用这些记录ID构建查询。

这并不理想,但由于你只是采摘ids,影响并不是太糟糕。

user_ids = User.where(status: :active).limit(10).pluck(:id) + User.where(status: :declined).limit(10).pluck(id)
users = User.where(id: user_ids)

答案 3 :(得分:0)

我认为您可以使用UNION。安装active_record_union并将or替换为union

User.where(status: :active).limit(10).union(User.where(status: :declined).limit(10))