Как суммировать все столбцы в ассоциации и разделить на количество, чтобы получить среднее значение в postgesql / rails - PullRequest
0 голосов
/ 21 марта 2019
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_at и end_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')

Это "работает", но дает мне разницу в секундах для каждого посещения, а не в среднем для всех.Допустим, у меня есть четыре посещения, 2 из которых составляют 2 часа, 1 - 4 часа и 1 - 1 час;в общей сложности 9 часов.Значение avg_time_spent должно составлять 2,25 часа.Спасибо за любую помощь, которая может быть предложена.

  • Я попробовал некоторые ответы, найденные на SO.Я пробовал подзапрос с UNION ALL, GROUPING SETS безрезультатно

Ответы [ 2 ]

1 голос
/ 21 марта 2019

В зависимости от того, с какой стороны вы пришли, вы можете сделать это следующими способами:

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

Создает хэш с комбинацией пользователя и идентификатора здания в качестве ключа и среднего времени посещения в качестве значения.

Если вы пришли от одного пользователя:

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

Надеюсь, вышесказанное вдохновит вас. Этот ответ работает только с идентификаторами, чтобы сделать запрос простым. Если вы хотите, чтобы весь экземпляр был установлен в качестве ключа, вы можете найти их с помощью отдельного запроса.


Примером этого может быть:

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
0 голосов
/ 21 марта 2019

Вы можете попробовать использовать функцию усреднения 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]}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...