web-dev-qa-db-ja.com

日付フィールドの年、日、または月によるActiveRecordの検索

日付属性を持つActiveRecordモデルがあります。その日付属性を利用して、年、日、月ごとに検索することは可能ですか?

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

または、単にfind_by_date(2012-12-1)を実行できますか。

年、月、日の属性を作成しないようにしたいと思っていました。

64
Mutuelinvestor

「日付属性」が(完全なタイムスタンプではなく)日付であると仮定すると、単純なwhereが「日付による検索」を提供します。

Model.where(:date_column => date)

find_by_date_columnは1つの結果しか得られないため、必要ありません。

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はデータベース間のすべての違いからあなたを保護しませんので、些細なことをしないとすぐに、独自の移植性レイヤーを構築する必要があります(ugいですが時には必要です)、データベースの限られたセットに結び付けます任意のデータベースで実行する必要があるものをリリースするか、またはRuby(非自明なデータ量に対しては非常識)ですべてのロジックを実行します。

大きなテーブルでは、年、月、および曜日のクエリはかなり遅くなります。一部のデータベースでは、関数の結果にインデックスを追加できますが、ActiveRecordはそれらを理解するには愚かすぎるため、それらを使用しようとすると大きな混乱を招きます。そのため、これらのクエリが遅すぎる場合は、回避しようとしている3つの列を追加する必要があります。

これらのクエリを頻繁に使用する場合は、クエリのスコープを追加できますが、 引数付きのスコープを追加する推奨方法 はクラスメソッドを追加するだけです:

スコープの引数を受け取るには、クラスメソッドを使用することをお勧めします。これらのメソッドは、関連オブジェクトで引き続きアクセスできます...

したがって、次のようなクラスメソッドがあります。

def self.by_year(year)
    where('extract(year from date_column) = ?', year)
end
# etc.
155
mu is too short

データベースに依存しないバージョン...

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
29
BSB

MySQLの場合はこれを試してください

Model.where("MONTH(date_column) = 12")
Model.where("YEAR(date_column) = 2012")
Model.where("DAY(date_column) = 24")
11
Beena Shetty

モデル内:

scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }

コントローラーで:

@courses = Course.by_year(params[:year])
10
张健健

これを行う最も簡単な方法はスコープだと思います:

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
Veraticus

これを試して:

Model.where("MONTH(date_column) = ? and DAY(date_column) = ?", DateTime.now.month, DateTime.now.day)
3
monteirobrena

ちょうど年のためのシンプルな実装

app/models/your_model.rb

scope :created_in, ->(year) { where( "YEAR( created_at ) = ?", year ) }

使用法

Model.created_in(1984)
2
Joshua Pinter

これは別のものです:

def self.by_year(year)
  where("published_at >= ? and published_at <= ?", "#{year}-01-01", "#{year}-12-31")
end

私にとってSQLiteでかなりうまく機能します。

1
Tintin81

私は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)
1
idrinkpabst

以下に示す別の短いスコープ:

  class Leave
    scope :by_year, -> (year) {
      where(date:
                Date.new(year).beginning_of_year..Date.new(year).end_of_year)
    }
  end

スコープを呼び出します:

Leave.by_year(2020)
0
Shiko

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)
0
heriberto perez