У меня есть модель Sleep
, экземпляры которой belongs_to
экземпляр модели Person
.Я хочу перейти к вычислению статистики в фоновом потоке.Люди сами сообщают свои данные и могут пропустить несколько дней.
Я создал модель Sleepstat
и планирую рассчитать некоторую статистику для каждого дня, для которого имеется один или несколько зарегистрированных экземпляров Sleep
.Люди могут вернуться и отредактировать свои данные позже, поэтому в этой фоновой задаче я хочу просканировать существующие экземпляры Sleepstat
, чтобы определить состояние флага needs_updating
.
Если кто-то создает запись Sleep
в день, для которого нет существующего Sleepstat
, тогда я хочу, чтобы фоновая задача создала Sleepstat
и вычислила статистику за этот день.Если кто-то добавляет дополнительную запись Sleep
к дню, в котором есть существующий Sleepstat
, тогда я хочу пометить Sleepstat
как нужное обновление и обновить его новыми данными, чтобы поддерживать статистику в актуальном состоянии.
Я думал сделать следующее:
Запустить запрос, чтобы вернуть все Sleep
записи, принадлежащие рассматриваемому Person
.Для этого я использовал этот запрос, который работает так, как я и ожидал:
all_sleeps = Sleep.select('start_time,end_time,multiday,time_zone,in_progress').where(:person_id => self.id)
Создать массив уникальных start_time
дат:
days_recorded = []
for sleep in all_sleeps
days_recorded.push sleep.start_time.to_date
end
days_recorded = days_recorded.uniq
Для каждого из days_recorded
посмотрите, существует ли Sleepstat
.Если нет, создайте его и рассчитайте статистику.Если это так, проверьте, является ли оно needs_updating
.Если это так, рассчитайте статистику.Если нет, то перейдите к следующему элементу в days_recorded
.
days_recorded.each do |d|
stat = Sleepstat.where(:date => d).first
if stat.nil?
# No record, so create one because we have data for that day and calculate stats
...
else
# There is a record. Evaluate whether it needs to be updated
if stat.needs_updating?
# Update the Sleepstat
...
end
end
end
Этот подход приводит к множеству независимых запросов:
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-10'
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-11'
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-12'
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-13'
MyИдея заключалась в том, чтобы попытаться сначала захватить все Sleepstat
с помощью запроса, подобного:
existing_stats = Sleepstat.where(:date => days_recorded)
, а затем выполнить итерацию по ним на шаге 3. Моя попытка выглядела так:
existing_stats = Sleepstat.where(:date => days_recorded)
days_recorded.each do |d|
stat = existing_stats.where(:date => d)
if stat.nil? || stat.length == 0
# No record, so create one because we have data for that day and calculate stats
...
else
# There is a record. Evaluate whether it needs to be updated
if stat.needs_updating?
# Update the Sleepstat
...
end
end
end
Это привело к большому количеству более сложных отдельных запросов:
Sleepstat Load (0.5ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-10'
Sleepstat Load (0.9ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-11'
Sleepstat Load (0.6ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-12'
Sleepstat Load (0.4ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-13'
Как повысить эффективность этого процесса, чтобы я не обращался к базе данных так много раз?