ActiveRecord在date字段中按年,日或月查找

我有一个具有date属性的ActiveRecord模型。 是否有可能利用该date属性按年,日和月查找:

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

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

我希望能避免创build年,月和日属性。

假设你的“date属性”是一个date(而不是一个完整的时间戳),那么一个简单的where会给你你的“按date查找”:

 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格式说明符将在某些情况下添加前导零,并且可能会混淆相等性testing,因此将cast(... as int)强制格式化的string转换为数字。

ActiveRecord不会保护你免受数据库之间的所有差异的影响,所以一旦你做了任何不重要的事情,你必须build立你自己的可移植性层(丑陋但有时是必需的),把自己绑定到一个有限的数据库集(除非你发布了一些必须在任何数据库上运行的东西),或者在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 

试试这个

 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的开始date和结束date之间的所有模型。

我喜欢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.rb

 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)