ActiveRecord найти по году, Дню или месяцу в поле даты


У меня есть модель ActiveRecord, которая имеет атрибут даты. Можно ли использовать этот атрибут даты, чтобы найти по году, дню и месяцу:

Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)

или просто можно найти find_by_date (2012-12-1).

Я надеялся, что смогу избежать создания атрибутов года, месяца и Дня.

10 58

10 ответов:

предполагая, что ваш "атрибут" дата "" дата (а не полной меткой времени), то просто where даст вам ваш "найти по дате":

Model.where(:date_column => date)

не хочешь find_by_date_column а что даст один результат.

для запросов года, месяца и дня вы хотели бы использовать extract функция SQL:

Model.where('extract(year  from date_column) = ?', desired_year)
Model.where('extract(month from date_column) = ?', desired_month)
Model.where('extract(day   from date_column) = ?', desired_day_of_month)

однако, если вы используете SQLite, вам придется возиться с strftime так как он не знает, что extract это:

Model.where("cast(strftime('%Y', date_column) as int) = ?", desired_year)
Model.where("cast(strftime('%m', date_column) as int) = ?", desired_month)
Model.where("cast(strftime('%d', date_column) as int) = ?", desired_day_of_month)

The %m и %d спецификаторы формата будут добавлять ведущие нули в некоторых случаях, и это может запутать тесты равенства, следовательно,cast(... as int) для принудительного преобразования форматированных строк в числа.

ActiveRecord не защитит вас от всех различий между базами данных, поэтому, как только вы сделаете что-нибудь нетривиальное, вам либо придется построить свой собственный уровень переносимости (уродливый, но иногда необходимый), привязать себя к ограниченному набору баз данных (реалистичный если вы не выпускаете что-то, что должно работать на любой базе данных), или сделать всю свою логику в Ruby (безумно для любого нетривиального количества данных).

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

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

использование метода класса является предпочтительным способом принятия аргументов для областей. Эти методы по-прежнему будут доступны для объектов ассоциации...

таким образом, у вас будут методы класса, которые выглядят так это:

def self.by_year(year)
    where('extract(year from date_column) = ?', year)
end
# etc.

независимая версия базы данных ...

def self.by_year(year)
  dt = DateTime.new(year)
  boy = dt.beginning_of_year
  eoy = dt.end_of_year
  where("published_at >= ? and published_at <= ?", boy, eoy)
end

для MySQL попробуйте это

Model.where("MONTH(date_column) = 12")
Model.where("YEAR(date_column) = 2012")
Model.where("DAY(date_column) = 24")

в вашей модели:

scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }

в вашем контроллере:

@courses = Course.by_year(params[:year])

Я думаю, что самый простой способ сделать это-это область:

scope :date, lambda {|date| where('date_field > ? AND date_field < ?', DateTime.parse(date).beginning_of_day, DateTime.parse(date).end_of_day}

используйте его так:

Model.date('2012-12-1').all

это вернет все модели с полем date_field между началом и концом дня для даты отправки.

Мне нравится ответ BSB, но как область

scope :by_year, (lambda do |year| 
  dt = DateTime.new(year.to_i, 1, 1)
  boy = dt.beginning_of_year
  eoy = dt.end_of_year
  where("quoted_on >= ? and quoted_on <= ?", boy, eoy)
end)

Это будет еще один:

def self.by_year(year)
  where("published_at >= ? and published_at <= ?", "#{year}-01-01", "#{year}-12-31")
end

работает довольно хорошо для меня в SQLite.

попробуйте это:

Model.where("MONTH(date_column) = ? and DAY(date_column) = ?", DateTime.now.month, DateTime.now.day)

простая реализация всего за год

приложение / модели / your_model.РБ

scope :created_in, ->(year) { where( "YEAR( created_at ) = ?", year ) }

использование

Model.created_in(1984)

Не нужно extract метод, это работает как шарм в postgresql:

text_value = "1997" # or text_value = '1997-05-19' or '1997-05' or '05', etc
sql_string = "TO_CHAR(date_of_birth::timestamp, 'YYYY-MM-DD') ILIKE '%#{text_value.strip}%'"
YourModel.where(sql_string)