Ich habe ein ActiveRecord-Modell mit einem Datumsattribut. Ist es möglich, dieses Datumsattribut zu verwenden, um nach Jahr, Tag und Monat zu suchen:
Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)
oder ist es einfach möglich ,_by_date (2012-12-1) zu finden.
Ich hatte gehofft, ich könnte es vermeiden, Attribute für Jahr, Monat und Tag zu erstellen.
ruby-on-rails
ruby-on-rails-3
activerecord
rails-activerecord
Mutuelinvestor
quelle
quelle
Antworten:
Angenommen, Ihr "Datumsattribut" ist ein Datum (und kein vollständiger Zeitstempel), dann erhalten Sie mit einem einfachen
where
"Suchen nach Datum":Model.where(:date_column => date)
Sie wollen nicht,
find_by_date_column
da dies höchstens ein Ergebnis liefert.Für die Abfragen für Jahr, Monat und Tag möchten Sie die
extract
SQL-Funktion verwenden: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)
Wenn Sie jedoch SQLite verwenden, müssen Sie damit herumspielen,
strftime
da es nicht weiß, was esextract
ist: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)
Die
%m
und%d
Format-Spezifizierer fügen in einigen Fällen führende Nullen hinzu, was die Gleichheitstests verwirren kann, daher müssencast(... as int)
die formatierten Zeichenfolgen zu Zahlen gezwungen werden.ActiveRecord schützt Sie nicht vor allen Unterschieden zwischen Datenbanken. Sobald Sie also etwas Nicht-Triviales tun, müssen Sie entweder eine eigene Portabilitätsschicht erstellen (hässlich, aber manchmal notwendig) und sich an eine begrenzte Anzahl von Datenbanken binden (realistisch, es sei denn Sie veröffentlichen etwas, das in einer beliebigen Datenbank ausgeführt werden muss, oder führen Ihre gesamte Logik in Ruby aus (verrückt nach nicht trivialen Datenmengen).
Die Abfragen für Jahr, Monat und Tag des Monats sind in großen Tabellen ziemlich langsam. In einigen Datenbanken können Sie Indizes für Funktionsergebnisse hinzufügen, aber ActiveRecord ist zu dumm, um sie zu verstehen, sodass es zu einem großen Durcheinander kommt, wenn Sie versuchen, sie zu verwenden. Wenn Sie also feststellen, dass diese Abfragen zu langsam sind, müssen Sie die drei zusätzlichen Spalten hinzufügen, die Sie vermeiden möchten.
Wenn Sie diese Abfragen häufig verwenden, können Sie Bereiche für sie hinzufügen. Die empfohlene Methode zum Hinzufügen eines Bereichs mit einem Argument besteht jedoch darin, nur eine Klassenmethode hinzuzufügen:
Sie hätten also Klassenmethoden, die so aussehen:
def self.by_year(year) where('extract(year from date_column) = ?', year) end # etc.
quelle
CAST
die Datumsnummer auf eine Ganzzahl setzen musste, anstatt 0 hinzuzufügen (das Hinzufügen von 0 gab mir aus irgendeinem Grund die falsche Nummer). Meine überarbeiteten Filter sind dann: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)
cast(... as int)
ist auf jeden Fall besser als ein Trick. Ich bin mir nicht sicher, warum ich ihn überhaupt nicht verwendet habecast
.TO_CHAR
mitILIKE
nur einer in der Linie, habe ich geschrieben das volle Beispiel am Ende dieses ThreadsDatenbankunabhängige Version ...
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
quelle
beginning_of_day
/end_of_day
undbeginning_of_month
/end_of_month
für die anderen Implementierungen.In Ihrem Modell:
scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }
In Ihrem Controller:
@courses = Course.by_year(params[:year])
quelle
Probieren Sie dies für MySQL aus
Model.where("MONTH(date_column) = 12") Model.where("YEAR(date_column) = 2012") Model.where("DAY(date_column) = 24")
quelle
Ich denke, der einfachste Weg, dies zu tun, ist ein Bereich:
scope :date, lambda {|date| where('date_field > ? AND date_field < ?', DateTime.parse(date).beginning_of_day, DateTime.parse(date).end_of_day}
Verwenden Sie es so:
Model.date('2012-12-1').all
Dadurch werden alle Modelle mit einem Datumsfeld zwischen dem Beginn und dem Ende des Tages für das von Ihnen gesendete Datum zurückgegeben.
quelle
'd >= ? and d < ?', dt.beginning_of_day, dt.tomorrow.beginning_of_day
. Verwenden<
und>
verfehlt die Grenze zwischen den Tagen.Einfache Implementierung für nur das Jahr
app/models/your_model.rb
scope :created_in, ->( year ) { where( "YEAR( created_at ) = ?", year ) }
Verwendung
Model.created_in( 1984 )
quelle
Versuche dies:
Model.where("MONTH(date_column) = ? and DAY(date_column) = ?", DateTime.now.month, DateTime.now.day)
quelle
MONTH
Funktion nicht. Versuchen Sie es mit extract oder date_part. wiki.postgresql.org/wiki/MONTH()_equivalent postgresqltutorial.com/postgresql-date_partIch mag die Antwort von BSB, aber als Umfang
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)
quelle
Dies wäre eine andere:
def self.by_year(year) where("published_at >= ? and published_at <= ?", "#{year}-01-01", "#{year}-12-31") end
Funktioniert ziemlich gut für mich in SQLite.
quelle
Ein weiterer kurzer Bereich wie folgt:
class Leave scope :by_year, -> (year) { where(date: Date.new(year).beginning_of_year..Date.new(year).end_of_year) } end
Nennen Sie den Bereich:
Leave.by_year(2020)
quelle
Keine
extract
Methode erforderlich , dies funktioniert wie ein Zauber in 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)
quelle
In Postgresql v 11+ (ich habe getestet): unten funktioniert
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)
quelle
Ich benutze normalerweise:
Model.where('extract(month from birthday_column) = ? and extract(day from birthday_column) = ?', Time.now.month, Time.now.day)
quelle
Wenn Sie nur nach dem Datum suchen, tun Sie einfach:
Model.where(date: Date.today.beginning_of_day..Date.today.end_of_day)
quelle