How can I select all database entries with a given date and month in a datetime? I tried this:
scope :with_year_and_month, ->(year, month) {
where("YEAR(created_at) = ? AND MONTH(created_at) = ?", month, year)
}
Now that would work perfectly fine in a MySQL database, but in an Sqlite database it fails because Sqlite hasn't implemented the MONTH() and YEAR() functions. The following works fine though in Sqlite:
scope :with_year_and_month, ->(year, month) {
where("strftime('%m', created_at) = ? AND strftime('%Y', created_at) = ?", "%02d" % month, year.to_s)
}
How would I do this in a database agnostic way?
I think you should let active record handle this, since it checks what database it's using and uses a driver for each database, the trick is to find a generic query to run, for me this is what I could come up with, might not be the best but I'm open for suggestions
scope :with_year_and_month, ->(year, month) {
where(created_at: Date.new(year,month,1)..Date.new(year,month,-1))
}
This will generate a between query
WHERE created_at BETWEEN '2015-02-01' AND '2015-02-28'
I think this should work with you
If created_at is DateTime then you should replace the Date with DateTime object so you don't miss the time between 2015-02-28 00:00:00 and 2015-02-28 23:59:59, but you'll need extra parameters for the time
scope :with_year_and_month, ->(year, month) {
where(created_at: DateTime.new(year,month,1)..DateTime.new(year,month,-1, -1, -1, -1))
}
of course you could create a small helper function to return those dates and shorten your method.
The result would be
Where created_at BETWEEN '2015-02-01 00:00:00' AND '2015-02-28 23:59:59'
Update:
So after a couple of comments, here's the final version
scope :with_year_and_month, ->(year, month) {
date = DateTime.new(year,month)
where(created_at: date...date.next_month)
}
The generated query would look like this
created_at >= '2015-02-01 00:00:00' AND created_at < '2015-03-01 00:00:00'
Related
Normally, to select all the records with the same date we use something like this :
Bronze.where(regdate: Date.new(2014,03,03))
How can we use where to select only from the year and month?
For month it would be:
date = # set your date here
Bronze.where(regdate: date.beginning_of_month...date.beginning_of_month + 1.month)
Analogically for year.
Or simply
Bronze.where(regdate: Date.new(2014,03,03)).select("YEAR(d) as year, MONTH(d) as month")
Where d is the column you want to extract year and month.
If you are using mysql then try extract function of SQL
Bronze.where('extract(year from regdate) = ? AND extract(month from regdate) = ?', year_parameter, month_parameter)
But if you are using sqlite then try this
Bronze.where("strftime('%Y', regdate) = ? AND strftime('%m', regdate) + 0 = ?", year_parameter, month_parameter)
I have a voting system. Each vote has a score.
I want to be able to count how many votes were made per day?
The code below adds the scores of each vote for each day and returns a hash with the total and the date as a key:
ratings = where(created_at: start.beginning_of_day..Time.zone.now).group("created_at").select("created_at, sum(score) as total_score")
ratings.each_with_object({}) do |rating, scores|
scores[rating.created_at.to_date] = rating.total_score
end
I want to be able to count how many votes were made on each day. Any ideas?
You can do what you want using a much nicer syntax:
from = start.beginning_of_day
to = Time.zone.now
Rating.group('date(created_at)').where(created_at: from .. to).count(:score)
This will return a hash having the date(created_at) as a key and count(:score) as value, for each day.
Example:
{ "2014-01-21" => 100, "2014-01-22" => 1213 }
The corresponding SQL is:
SELECT COUNT("ratings"."score") AS count_score, date(created_at) AS date_created_at FROM "ratings" WHERE ("ratings"."created_at" BETWEEN '2014-01-21' AND '2014-01-24') GROUP BY date(created_at)
The corresponding MySQL query would be
SELECT date(created_at) as date, count(*) as count FROM `ratings` WHERE (created_at between '2014-01-21 00:00:00' AND '2014-01-24 08:16:46') GROUP BY date(created_at)
The Rails version is:
start_date = Date.parse("2014-01-21").beginning_of_day
end_time = Time.zone.now
Rating.group('date(created_at)').select('date(created_at) as date, count(*) as count').where(["created_at between ? AND ?", start_time, end_time])
I am searching records in user model. The search attributes from_date, to_date will be used to searched records in user model based on created_at column.
User model : (id, name, created_at)
I have the following records in the database.
id, name, created_at
1 jd1 2013-09-04 18:01:57
2 jd2 2013-09-05 19:01:57
3 jd3 2013-09-05 23:01:57
When i am searching, between "2013-09-04".to_date(from_date) and "2013-09-05".to_date(to_date), only the first two records are being returned. The last one is not being returned. When i change the to_date to "2013-09-06" the last record is showing. This is the query i used.
date_range = from_date ... to_date + 1.day
scope :by_date, ->(date_range) {where(created_at: date_range)}
User.by_date(date_range)
What is wrong with the query? I think there is daylight time zone issue with it.
You need to cast your created_at fields as Date:
CAST(users.created_at AS DATE)
In the where clause:
where("CAST(users.created_at AS DATE) BETWEEN ? AND ?", date1, date2 )
In your case, with your scope:
scope :by_date, lambda{ |date_range| where("CAST(users.created_at AS DATE) BETWEEN ? AND ?", date_range.min, date_range.max ) }
Hope this helps!
Bonus: The short version of CAST, works with PostGre SQL:
scope :by_date, lambda{ |date_range| where("users.created_at::date BETWEEN ? AND ?", date_range.min, date_range.max ) }
I have a model (Entries) with five years worth of records (one record per day). I need a method that, when passed a date object such as 2011-12-25 00:00:00, will show me ALL the records that have happened on 12/25 (querying against the :created_at column), regardless of the year or time that's passed.
RoR 3.0.9 / Ruby 1.9.2p290
You can use the MONTH and DAY values of mysql. Maybe something like:
Model.where("MONTH(created_at) = ? and DAY(created_at) = ?", somedate.month, somedate.day)
A general solution that should work with most SQL databases (include MySQL and PostgreSQL):
Entry.where('extract(month from created_at) = ? AND extract(day from created_at) = ?', d.month, d.day)
SQLite doesn't understand extract though so you'd have to use:
Entry.where("strftime('%m/%d', created_at) = ?", d.strftime('%m/%d'))
Assuming that you are using mysql:
User.where(["DAY(created_at) = ? AND MONTH(created_at) = ?", date.day, date.month])
Assume RDBMS is MySQL and you have form with combobox to select month and/or date_of_months, may be you could make a named_scope, for example :
scope :by_date_or_month, lambda { |date, month| {:conditions => ["DAYOFMONTH(created_at) = ? or MONTH(created_at) = ?", date, month]}}
Test from IRB :
Model.by_date_or_month(31,8)
Model.by_date_or_month(nil,8)
I have a date column in my database in a Rails 3.1 app, and I want to be able to get the records where the date's year matches a specific year.
I tried where(:date.year == year) but of course I got NoMethodError: undefined method 'year' for :date:Symbol. Is it possible to do this type of query?
You can use a scope to build something like:
scope :for_year, lambda {|date| where("date >= ? and date <= ?", "#{date.year}0101", "#{date.year}1231")}
In your Model:
scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }
In your Controller:
#courses = Course.by_year(params[:year])
Jesse gave you, I think, the idea for the actual solution, but to explain why this failed - it's because it tried to evaluate ".year" as a method on the symbol you passed it: ":date".
The word :date is just a parameter to tell "where" which value it will later use to construct the SQL query to pass to the db.
It doesn't turn into the actual date of the record. But the ".year" will evaluate as you're passing it as a parameter, before anything has been done with the ":date" symbol.
Assuming your date format is: YYYY-MM-DD HH:MM:SS
Try this:
where(Date.strptime(:date, "%Y-%m-%d %H:%M:%S").year == year)
OR
where(["YEAR(?) = ?", :date, year])