如何对关联中的所有列求和并除以计数以获取postgesql / rails中的平均值

时间:2019-03-21 09:47:10

标签: ruby-on-rails postgresql

class User < ApplicationRecord
  has_many :visits
end

class Visit < ApplicationRecord
  belongs_to :user
  belongs_to :building
end

class Building < ApplicationRecord
  has_many :visits
end

我有上述关系。访问具有start_atend_at,它们是时间戳。我试图找出用户在建筑物上花费的平均时间。我有以下查询。

visits.select('(SUM(CAST(EXTRACT(EPOCH FROM end_at) AS integer)) - SUM(CAST(EXTRACT(EPOCH FROM start_at) AS integer))) / COUNT(visits) AS avg_time_spent')

这是“有效的”方法,但它仅给我每次访问的秒数差异,而不是所有访问的平均值。假设我有4次造访,其中2次造访2小时,1次造访4小时,1次造访1小时;总共9个小时。 avg_time_spent 应该为2.25小时。感谢您提供的任何帮助。

  • 我尝试了一些在SO上找到的答案。我尝试了UNION ALLGROUPING SETS的子查询无济于事

2 个答案:

答案 0 :(得分:1)

根据您来自哪个方面,可以通过以下方式进行操作:

Visit
  .group(:user_id, :building_id)
  .pluck(:user_id, :building_id, 'AVG("visits"."end_at" - "visits"."start_at")')
  .map { |*ids, visit_duration| [ids, visit_duration] }
  .to_h

使用用户和建筑物ID作为键,平均访问时间作为值来生成哈希。

如果您来自单个用户:

user.visits.group(:building_id)
    .pluck(:building_id, 'AVG("visits"."end_at" - "visits"."start_at")')
    .to_h

或者如果您来自建筑物:

building.visits.group(:user_id)
        .pluck(:user_id, 'AVG("visits"."end_at" - "visits"."start_at")')
        .to_h

我希望以上内容能给您一些启发。该答案仅适用于ID,以保持查询简单。如果要将整个实例设置为键,则可以使用单独的查询来查找它们。


例如:

average_time = user.visits # ...

buildings = average_time.keys.zip(Building.find(average_time.keys)).to_h
average_time.transform_keys! { |building_id| buildings[building_id] }

# The simpler approach
#
#     average_time.transform_keys!(&:Building.method(:find))
#
# results in a 1+N query

答案 1 :(得分:0)

您可以尝试使用postgres的平均函数AVG

averaged = user.visits.select("AVG(end_at - start_at) as average_interval, building_id").group(:building_id).preload(:building)
averaged.map{|o| [o.building, o.average_interval]}