我有一个具有日期属性的ActiveRecord模型。是否可以利用该日期属性按年,日和月查找:
Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)
或者只是可以find_by_date(2012-12-1)。
我希望我可以避免创建年,月和日属性。
答案 0 :(得分:155)
假设您的“日期属性”是日期(而不是完整时间戳),那么简单的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)
%m
和%d
格式说明符会在某些情况下添加前导零,这会混淆相等性测试,因此cast(... as int)
会强制格式化字符串为数字。
ActiveRecord不会保护您免受数据库之间的所有差异的影响,因此只要您执行任何非常重要的操作,您就必须构建自己的可移植层(丑陋但有时必要),将自己绑定到一组有限的数据库中(除非你发布必须在任何数据库上运行的东西,否则是现实的),或者用Ruby完成所有逻辑(对于任何非常重要的数据都是疯狂的)。
年份,月份和日期查询在大型表格上会相当慢。有些数据库允许你在函数结果上添加索引,但是ActiveRecord太愚蠢而无法理解它们,所以如果你试图使用它们会很麻烦;所以,如果你发现这些查询太慢,那么你必须添加你想要避免的三个额外列。
如果你要经常使用这些查询,那么可以为它们添加范围,但recommended way to add a scope with an argument只是添加一个类方法:
使用类方法是接受范围参数的首选方法。仍然可以在关联对象上访问这些方法......
所以你的类方法看起来像这样:
def self.by_year(year)
where('extract(year from date_column) = ?', year)
end
# etc.
答案 1 :(得分:28)
数据库独立版......
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
答案 2 :(得分:10)
对于MySQL试试这个
Model.where("MONTH(date_column) = 12")
Model.where("YEAR(date_column) = 2012")
Model.where("DAY(date_column) = 24")
答案 3 :(得分:10)
在你的模特中:
scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }
在您的控制器中:
@courses = Course.by_year(params[:year])
答案 4 :(得分:5)
我认为最简单的方法是范围:
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。
答案 5 :(得分:3)
试试这个:
Model.where("MONTH(date_column) = ? and DAY(date_column) = ?", DateTime.now.month, DateTime.now.day)
答案 6 :(得分:2)
应用/模型/ your_model.rb 强>
scope :created_in, ->(year) { where( "YEAR( created_at ) = ?", year ) }
<强>用法强>
Model.created_in(1984)
答案 7 :(得分:1)
我喜欢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)
答案 8 :(得分:1)
这将是另一个:
def self.by_year(year)
where("published_at >= ? and published_at <= ?", "#{year}-01-01", "#{year}-12-31")
end
在SQLite中对我很有用。
答案 9 :(得分:0)
不需要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)
答案 10 :(得分:0)
另一个简短的范围如下:
for
调用范围:
class Leave
scope :by_year, -> (year) {
where(date:
Date.new(year).beginning_of_year..Date.new(year).end_of_year)
}
end
答案 11 :(得分:0)
在Postgresql v 11+中(我已经测试过):以下方式有效
Model.where('extract(year from date_column::date) = ?', desired_year)
Model.where('extract(month from date_column::date) = ?', desired_month)
Model.where('extract(day from date_column::date) = ?', desired_day_of_month)
答案 12 :(得分:0)
我通常使用:
Model.where('extract(month from birthday_column) = ? and extract(day from birthday_column) = ?', Time.now.month, Time.now.day)
答案 13 :(得分:0)
如果您只是在寻找日期,只需执行以下操作:
Model.where(date: Date.today.beginning_of_day..Date.today.end_of_day)