Как создать модуль «Предстоящие дни рождения» в Rails? - PullRequest
4 голосов
/ 29 июня 2009

У меня есть таблица «пользователи» со столбцом «date_of_birth» (формат DATE с днем, месяцем, годом). Во внешнем интерфейсе мне нужно перечислить 5 предстоящих дней рождения.

Потратил целую вечность, пытаясь выработать логику .. также просмотрел все возможные статьи в Google без удачи ..

Есть предложения, как это сделать в RoR?

Спасибо!

Ответы [ 9 ]

8 голосов
/ 29 июня 2009

В нескольких ответах предлагалось вычислить / сохранить день года и отсортировать по ним, но одно это не принесет пользы, когда вы близки к концу года и должны учитывать людей Дни рождения в начале следующего года.

Я бы выбрал решение, в котором вы вычисляете полную дату (год, месяц, день) следующего дня рождения каждого человека , а затем сортируете по ней. Вы можете сделать это в коде Ruby или с помощью хранимой процедуры в базе данных. Последнее будет быстрее, но позже вам будет сложнее перейти на другую платформу БД.

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

class User
  def next_birthday
    year = Date.today.year
    mmdd = date_of_birth.strftime('%m%d')
    year += 1 if mmdd < Date.today.strftime('%m%d')
    mmdd = '0301' if mmdd == '0229' && !Date.parse("#{year}0101").leap?
    return Date.parse("#{year}#{mmdd}")
  end
end

users = User.find(:all, :select => 'id, date_of_birth').sort_by(&:next_birthday).first(5)

Редактировать : исправлена ​​работа с високосными годами.

4 голосов
/ 16 октября 2013

Благодаря этому сообщению в моем приложении rails 3, которое я использую:

 u = User.where("strftime('%m%d', date_of_birth) = ?", Date.today.strftime('%m%d'))

Обновление:

Чтобы использовать это с Postgresql:

u = User.where("extract(month from date_of_birth) = ? AND extract(day from date_of_birth) = ?", Date.today.strftime('%m'), Date.today.strftime('%d'))
1 голос
/ 30 сентября 2014

Лучше использовать SQL , чтобы сделать этот запрос:

next_month = (Date.today + 1.month).month # or use any other integer

users_having_birhtday_next_month = 
  User.where("EXTRACT(MONTH FROM date_of_birth) = ?", next_month)

Примечание: EXTRACT - функция PostgreSQL

0 голосов
/ 13 октября 2018

Я обнаружил, что это работает для меня. Мне не требовалось фильтровать последние пять результатов, поэтому я использовал месяц такой же, как текущий.

def upcoming_birthdays
    @resident = current_business.residents.where("extract(month from date_of_birth) = ?", Date.today.strftime('%m')).paginate(:page => params[:page])
end
0 голосов
/ 23 августа 2017

Если ваша база данных mysql, следующее, вероятно, быстрее:

scope :birthday_next_week, (lambda {
  where('DayOfYear(date_of_birth) >= 7
     And DayOfYear(date_of_birth) - DayOfYear(curdate()) Between 0 and 6) Or
   (MOD(YEAR(curDate()),4) = 0) And MOD(YEAR(curDate()),100) != 0
    And (DayOfYear(date_of_birth) + 366 - DayOfYear(curdate())) % 366 < 7) Or
     (DayOfYear(date_of_birth) + 365 - DayOfYear(curdate())) % 365 < 7').
    order('DATE_FORMAT( date_of_birth, "%m-%d" ) ASC')
})

Редактировать: изменено, чтобы оно работало за неделю до новых / високосных годов.

0 голосов
/ 29 июня 2009

Вот как я нахожу сегодняшние дни рождения:

User.find_by_sql("select * from users where date_format(date_of_birth, '%m%d') = date_format(now(), '%m%d')")

Я запускаю это раз в день. Это займет менее секунды из примерно 100 000 строк. (Он неправильно обрабатывает людей, рожденных 29 февраля.)

0 голосов
/ 29 июня 2009

Если вы используете Oracle, вы можете сделать это без создания нового столбца. IMO - это запах создания столбца, который содержит данные, которые у вас уже есть.

SQL немного уродлив - я уверен, что есть более элегантный способ сделать это. Обычно в этих случаях я бы попросил совета у моих друзей из DBA.

User.find(:all,
  :conditions => 
    "TO_NUMBER(TO_CHAR(dob, 'MMDD')) >= TO_NUMBER(TO_CHAR(SYSDATE, 'MMDD'))",
  :order => "TO_NUMBER(TO_CHAR(dob, 'MMDD'))",
  :limit => 5)

Некоторые люди думают, что дубликат столбца быстрее, но , если у вас достаточно пользовательских данных, чтобы ускорить возникновение проблемы, вам следует сравнить сравнительный столбец с таблицей без таблицы с функциональным индексом TO_NUMBER(TO_CHAR(dob, 'MMDD')). .

0 голосов
/ 29 июня 2009

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

Лучше хранить месяц и день в виде строки: d.strftime ('% m% d'). Затем вы можете использовать это как (возможно) два запроса (при условии, что новый столбец будет «месяцем»)

Сначала

User.find(:all,
          :condition => [:monthday > Date.now.strftime('%m%d')],
          :select => "DISTINCT monthday",
          :limit => 5)

Если вы не получили 5 результатов, повторите запрос, за исключением использования «0101» вместо расчета даты и понижения лимита.

В результате вы получите список monthday строк, которые затем необходимо будет преобразовать в даты.

Если вы хотите, чтобы пользователи удалили строку :select.

0 голосов
/ 29 июня 2009

У меня будет обратный вызов before_save , который рассчитывает и сохраняет в базе данных день года вместе с днем ​​рождения.

Затем у вас есть простой запрос, чтобы отозвать следующие 5 дней рождения. Обязательно обработайте граничное условие, в котором вы находитесь в конце года (я бы проверил, не получили ли вы 5 результатов в RoR, а затем запустите новый запрос на 1 января, чтобы получить несколько дополнительных дней рождения, чтобы сделать это до 5).

Возможно, вы захотите кэшировать результаты, чтобы не повторять запрос, если он находится на общей странице.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...