如何编写一个Rails finder方法,它将返回按记录分组的最大日期?

时间:2017-09-01 17:00:47

标签: ruby-on-rails postgresql ruby-on-rails-5

我在PostGres 9.5中使用Rails 5。我有一张跟踪价格的表......

                                         Table "public.crypto_prices"
       Column       |            Type             |                         Modifiers
--------------------+-----------------------------+------------------------------------------------------------
 id                 | integer                     | not null default nextval('crypto_prices_id_seq'::regclass)
 crypto_currency_id | integer                     |
 market_cap_usd     | bigint                      |
 total_supply       | bigint                      |
 last_updated       | timestamp without time zone |
 created_at         | timestamp without time zone | not null
 updated_at         | timestamp without time zone | not null

我想获得所选货币的每种货币的最新价格(last_updated最大)。我可以找到与某些货币相关的所有价格,如此

current_prices = CryptoPrice.where(crypto_currency_id: CryptoIndexCurrency.all.pluck(:crypto_currency_id).uniq)

然后我可以按货币对数组进行排序,循环遍历每个数组,直到找到last_updated值最大的那个,但是我怎么能写一个每个货币只返回一行的查找器最大的last_updated日期?

编辑:尝试过Owl Max的建议

ids = CryptoIndexCurrency.all.pluck(:crypto_currency_id).uniq
crypto_price_ids = CryptoPrice.where(crypto_currency_id: ids).group(:crypto_currency_id).maximum(:last_updated).keys
puts "price ids: #{crypto_price_ids.length}"
@crypto_prices = CryptoPrice.where(crypto_currency_id: crypto_price_ids)
puts "ids: #{@crypto_prices.size}"

虽然第一个" put"只显示" 12"第二个结果显示超过38,000个结果。它应该只返回12个结果,每个货币一个。

4 个答案:

答案 0 :(得分:1)

我们可以编写一个查找器,每个货币只返回一行,其中last_updated日期最长,如

current_prices = CryptoPrice.where(crypto_currency_id: CryptoIndexCurrency.all.pluck(:crypto_currency_id).uniq).select("*, id as crypto_price_id, MAX(last_updated) as last_updated").group(:crypto_currency_id)

我希望这会让你更接近你的目标。谢谢。

答案 1 :(得分:0)

使用此代码,您可以从特定表中获取最新更新的行。

  CryptoPrice.order(:updated_at).pluck(:updated_at).last

这应该对你有帮助。

答案 2 :(得分:0)

由于or查询方法

,仅适用于Rails5
specific_ids = CryptoIndexCurrency.distinct.pluck(:crypto_currency_id)
hash = CryptoPrice.where(crypto_currency_id: specific_ids)
                  .group(:crypto_currency_id)
                  .maximum(:last_updated)
hash.each_with_index do |(k, v), i|
  if i.zero?
    res = CryptoPrice.where(crypto_currency_id: k, last_updated: v)
  else
    res.or(CryptoPrice.where(crypto_currency_id: k, last_updated: v))
  end
end

<强>解释

您可以使用group重新组合表格中每个CryptoPrice礼物的所有CryptoIndexCurrency个对象。

然后使用maximum(感谢@artgb)获取最大值last_updated。这将输出Hash,其中包含键:crypto_currency_id和值  last_updated

最后,您可以使用keys仅获得Array crypto_currency_id

CryptoPrice.group(:crypto_currency_id).maximum(:last_updated)
=> => {2285=>2017-06-06 09:06:35 UTC,
       2284=>2017-05-18 15:51:05 UTC,
       2267=>2016-03-22 08:02:53 UTC}

此解决方案的问题在于,您无需获取整个记录即可获得每行的最长日期。

要获取记录,您可以成对地对哈希进行循环。使用crypto_currency_idlast_updated。它是hacky但是我发现的唯一解决方案。

答案 3 :(得分:0)

在一个语句/查询中,这在Rails中目前并不容易。如果您不介意使用多个语句/查询,那么这是您的解决方案:

cc_ids = CryptoIndexCurrency.distinct.pluck(:crypto_currency_id)

result = cc_ids.map do |cc_id|
  max_last_updated = CryptoPrice.where(crypto_currency_id: cc_id).maximum(:last_updated)
  CryptoPrice.find_by(crypto_currency_id: cc_id, last_updated: max_last_updated)
end

您正在寻找map方法的结果。这会为每个crypto_currency_id和1个查询生成2个查询,以请求crypto_currency_id

如果您想使用一个查询执行此操作,则需要使用OVER (PARTITION BY ...)。有关此内容的更多信息,请访问以下链接:

但在这种情况下,您必须编写一些SQL。

编辑1:

如果你想要一个不错的Hash结果运行:

cc_ids.zip(result).to_h

编辑2:

如果您希望将查询量减半,可以将max_last_updated中的find_by查询推送为子查询,如下所示:

cc_ids = CryptoIndexCurrency.distinct.pluck(:crypto_currency_id)

result = cc_ids.map do |cc_id|
  CryptoPrice.find_by(<<~SQL.squish)
    crypto_currency_id = #{cc_id} AND last_updated = (
      SELECT MAX(last_updated) 
      FROM crypto_prices 
      WHERE crypto_currency_id = #{cc_id})
  SQL
end

这会为每个crypto_currency_id和1个查询生成1个查询,以请求crypto_currency_id