ActiveRecord在日期字段上按年,日或月查找

Mut*_*tor 66 activerecord ruby-on-rails ruby-on-rails-3 rails-activerecord

我有一个具有日期属性的ActiveRecord模型.是否可以使用该日期属性按年,日和月查找:

Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)
Run Code Online (Sandbox Code Playgroud)

或者只是可以find_by_date(2012-12-1).

我希望我可以避免创建年,月和日属性.

mu *_*ort 162

假设你的"日期属性"是一个日期(而不是一个完整的时间戳),那么一个简单的where将给你"按日期查找":

Model.where(:date_column => date)
Run Code Online (Sandbox Code Playgroud)

你不希望find_by_date_column因为这最多会给出一个结果.

对于年,月和日查询,您需要使用extractSQL函数:

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)
Run Code Online (Sandbox Code Playgroud)

但是,如果你使用的是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)
Run Code Online (Sandbox Code Playgroud)

%m%d格式说明会在某些情况下,添加前导零,并能迷惑平等的测试,因此cast(... as int)迫使格式化字符串的数字.

ActiveRecord不会保护您免受数据库之间的所有差异的影响,因此只要您执行任何不重要的操作,您就必须构建自己的可移植层(丑陋但有时必要),将自己绑定到一组有限的数据库(除非是现实的,否则你正在发布必须在任何数据库上运行的东西),或者在Ruby中做你所有的逻辑(对任何非常重要的数据都是疯狂的).

大型桌子上的年,月和日查询速度相当慢.有些数据库允许你在函数结果上添加索引,但是ActiveRecord太愚蠢而无法理解它们,所以如果你试图使用它们会很麻烦; 所以,如果你发现这些查询太慢,那么你将不得不添加你想要避免的三个额外列.

如果你要经常使用这些查询,那么你可以为它们添加范围,但是添加带参数的范围推荐方法就是添加一个类方法:

使用类方法是接受范围参数的首选方法.这些方法仍然可以在关联对象上访问...

所以你的类方法看起来像这样:

def self.by_year(year)
    where('extract(year from date_column) = ?', year)
end
# etc.
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的好回答。看来,您必须依靠数据库特定功能来克服activerecord的优势之一。也许这就是为什么您看到如此频繁地按月,日和年划分日期的原因。再次感谢。 (3认同)

BSB*_*BSB 29

数据库独立版......

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
Run Code Online (Sandbox Code Playgroud)

  • 对于其他实现,还有`beginning_of_day` /`end_of_day`和`beginning_of_month` /`end_of_month`. (4认同)

Bee*_*tty 11

对于MySQL试试这个

Model.where("MONTH(date_column) = 12")
Model.where("YEAR(date_column) = 2012")
Model.where("DAY(date_column) = 24")
Run Code Online (Sandbox Code Playgroud)

  • 这适用于MySQL,但不适用于PostgreSQL. (10认同)

张健健*_*张健健 10

在您的模型中:

scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }
Run Code Online (Sandbox Code Playgroud)

在您的控制器中:

@courses = Course.by_year(params[:year])
Run Code Online (Sandbox Code Playgroud)


小智 6

要查找与特定日期/月份/年份匹配的所有记录,您可以执行以下操作:

Model.where(date: Date.today.all_day)
Model.where(date: Date.today.all_month)
Model.where(date: Date.today.all_year)
Run Code Online (Sandbox Code Playgroud)

.all_X返回日期范围:

 Time.current.all_year
=> Sat, 01 Jan 2022 00:00:00.000000000 UTC +00:00..Sat, 31 Dec 2022 23:59:59.999999999 UTC +00:00
Run Code Online (Sandbox Code Playgroud)


Ver*_*cus 5

我认为最简单的方法是范围:

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

像这样使用它:

Model.date('2012-12-1').all
Run Code Online (Sandbox Code Playgroud)

这将返回所有模型,其中date_field位于您发送日期的开始和结束之间.

  • `'d> =?和d <?',dt.beginning_of_day,dt.tomorrow.beginning_of_day`.使用`<`和`>`错过了几天之间的界限. (2认同)

Jos*_*ter 5

Simple Implementation for Just The Year

app/models/your_model.rb

scope :created_in, ->( year ) { where( "YEAR( created_at ) = ?", year ) }
Run Code Online (Sandbox Code Playgroud)

Usage

Model.created_in( 1984 )
Run Code Online (Sandbox Code Playgroud)