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)