Rails中的ORDER BY和DISTINCT ON(...)

时间:2014-12-11 05:15:38

标签: ruby-on-rails postgresql active-model-serializers

我试图通过created_at进行ORDER,然后根据外键获取DISTINCT集。

另一部分是以某种方式使用它是ActiveModelSerializer。具体来说,我希望能够声明:

has_many :somethings

在序列化程序中。让我进一步解释......

我能够通过这个自定义sql获得我需要的结果:

def latest_product_levels
  sql = "SELECT DISTINCT ON (product_id) client_product_levels.product_id,       
  client_product_levels.* FROM client_product_levels WHERE client_product_levels.client_id = #{id} ORDER BY product_id, 
  client_product_levels.created_at DESC";
  results = ActiveRecord::Base.connection.execute(sql)
end

是否有任何可能的方法来获得此结果,但作为has_many关系的一个条件,以便我可以在AMS中使用它?

在伪代码中:@ client.products_levels 会做类似的事情:@client.order(created_at: :desc).select(:product_id).distinct

当然,由于我之外的原因而失败。

任何帮助都会很棒。

谢谢。

1 个答案:

答案 0 :(得分:3)

构建此问​​题的一种好方法是将查询分为两部分:第一部分管理行的过滤,以便您只获得最新的客户端产品级别。第二部分使用标准has_many关联将ClientClientProductLevel联系起来。

从您的ClientProductLevel模型开始,您可以创建一个范围来执行最新的过滤:

class ClientProductLevel < ActiveRecord::Base
  scope :latest, -> {
    select("distinct on(product_id) client_product_levels.product_id,
                                    client_product_levels.*").
    order("product_id, created_at desc")
  }
end

您可以在具有返回ClientProductLevel对象列表的查询的任何位置使用此范围,例如ClientProductLevel.latestClientProductLevel.where("created_at < ?", 1.week.ago).latest等。

如果您还没有这样做,请设置Client关联has_many课程:

class Client < ActiveRecord::Base
  has_many :client_product_levels
end

然后在你的ActiveModelSerializer中试试这个:

class ClientSerializer < ActiveModel::Serializer
  has_many :client_product_levels

  def client_product_levels
    object.client_product_levels.latest
  end
end

当您调用ClientSerializer序列化Client对象时,序列化程序会看到has_many声明,它通常会转发到您的Client对象,但是因为我们通过该名称获得了本地定义的方法,它会调用该方法。 (请注意,此has_many声明与ActiveRecord has_many不同,它指定了表之间的关系:在这种情况下,它只是说序列化程序应该在键下呈现一系列序列化对象`client_product_levels'。)

ClientSerializer#client_product_levels方法依次从客户端对象调用has_many关联,然后将latest范围应用于它。 ActiveRecord最强大的功能是它允许您将不同的组件链接到一个查询中。这里,has_many生成`where client_id = $ X'部分,范围生成查询的其余部分。瞧瞧!

在简化方面:ActiveRecord没有对distinct on的本机支持,所以你一直坚持使用自定义sql的那部分。我不知道您是否需要在您的select子句中明确包含client_product_levels.product_id,因为它已被*包含在内。您可以尝试转储它。