ActiveRecord найти по году, Дню или месяцу в поле даты
У меня есть модель ActiveRecord, которая имеет атрибут даты. Можно ли использовать этот атрибут даты, чтобы найти по году, дню и месяцу:
Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)
или просто можно найти find_by_date (2012-12-1).
Я надеялся, что смогу избежать создания атрибутов года, месяца и Дня.
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)