I need to do a query on Rails model time stamps (created_at and updated_at) finding all records after a specified date. When executing the query:
Item.where("created_at > :date OR updated_at > :date", date: "2011-05-29")
> SELECT "items".* FROM "items" WHERE (created_at > '2011-05-29' OR updated_at > '2011-05-29')
It includes records like:
attributes:
created_at: 2011-05-29 16:07:10.664227
updated_at: 2011-05-29 16:07:10.664229
However, this date isn't greater than the one specified. Any simple way to fix this query? Thanks.
Edit:
I've also tried doing:
Item.where("created_at > :date OR updated_at > :date", date: "2011-05-29 16:07:10")
With the same result, however adding the fraction of seconds stamp as well gives the correct result.
Say you want records starting 30th May:
Then search for records that are >= 2011-05-30 00:00:00:
some_date = Time.now
Item.where("created_at >= :date OR updated_at >= :date", date: some_date.tomorrow.beginning_of_day)
I think the problem is that the created_at and updated_at are datetime, so if you only pass a date it defaults the time to 00:00:00 and anything past that is obviously greater. So the records you are seeing are past that date since the hour is greater.
Take a look at your precision, you can either pass 1 more day, and I'll show anything above that day, even greater for a second, or simply pass the hour as 23:59:59.
You can use my gem by_star to do this.
Post.after(date)
Unfortunately SQL disagrees with you:
mysql> SELECT '2011-05-30 12:00:00' > '2011-05-30';
+--------------------------------------+
| '2011-05-30 12:00:00' > '2011-05-30' |
+--------------------------------------+
| 1 |
+--------------------------------------+
And for comparing dates with datetimes, MySQL has this to say:
A DATE value is coerced to the DATETIME type by adding the time portion as '00:00:00'.
Related
Below is the query to get the numbers of agents created in each month in last three months.
agents_per_month = Agents.where("created_at > ? AND created_at < ?", Date.today.at_beginning_of_month - 2.months, Date.today).group("date_trunc('month', created_at)").count
The result output is as follows:
{2020-07-01 00:00:00 UTC=>75, 2020-08-01 00:00:00 UTC=>31}
The issue with the above result is that since the last third month had no agents created so it doesn't show any value. But I want it to show result in the order as below:
{2020-06-01 00:00:00 UTC=>0, 2020-07-01 00:00:00 UTC=>75, 2020-08-01 00:00:00 UTC=>31}
So if a month doesn't have any value it should show 0 rather than no value shows up for the particular month.
Please help me resolve this issue.
You can join to a generate_series table using PostgreSQL:
class Agent < ApplicationRecord
def self.totals_in_last_three_months
joins("
RIGHT JOIN generate_series(
date_trunc('month', statement_timestamp() - interval'2 months'),
date_trunc('month', statement_timestamp()),
interval'1 month'
) as months(month)
ON date_trunc('month', agents.created_at) = months.month")
.group('months.month')
.count('agents.*')
end
end
I want to find signup count daily, for the date range say this month. so
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
dates = ((starts_at.to_date)..(ends_at.to_date)).to_a
dates.each_with_index do |date,i|
User.where("created_at >= ? and created_at <= ?", date, date.tomorrow)
end
So nearly 30 queries running, how to avoid running 30 query and do it in single query?
I need something like
group_by(:created_at)
But in group by if there is no data present for particular date it's showing nothing, but I need date and count as 0
I followed this:
How do I group by day instead of date?
def group_by_criteria
created_at.to_date.to_s(:db)
end
User.all.group_by(&:group_by_criteria).map {|k,v| [k, v.length]}.sort
Output
[["2016-02-05", 5], ["2016-02-06", 12], ["2016-02-08", 6]]
There is no data for 2016-02-05 so it should be included with count 0
I can't test it at the moment, but it should be possible to filter your date range and group it with a little help of your dbms like this:
User.select('DATE(created_at)').where("created_at >= ? and created_at <= ?", DateTime.now.beginning_of_month, DateTime.now.end_of_month).group('DATE(created_at)').count
Would this do?
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
# => {Tue, 09 Feb 2016=>151, Mon, 08 Feb 2016=>130}
Note that you won't get any results for dates when there has been zero creations, so you might want to do something like this:
Hash[*(starts_at..ends_at).to_a.flat_map{|d| [d, 0]}].merge(
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
)
Not pretty, but what happens there is you first create a hash with all dates in the range having zero values and merging the results from database into that hash.
date_start = Time.parse('11/08/2015').beginning_of_day
date_end = Time.parse('11/08/2015').end_of_day
created_at_day_tz = "date(created_at AT TIME ZONE \'UTC\'
AT TIME ZONE \'#{Time.zone.tzinfo.identifier}\')"
users = User.where("users.created_at BETWEEN ? AND ?", date_start, date_end)
Grouping by created_at as created_at_day (date only, new name for the groupped attribute)
grouped_with_timezone_day = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at_day, count(*) as count")
# grouped_with_timezone_day.map {|u| [u.created_at_day, u.count] }
# => [[Tue, 11 Aug 2015, 186]]
Grouping by created_at as created_at (date only, same name for the groupped attribute)
grouped_with_timezone = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at, count(*) as count")
# grouped_with_timezone.map {|u| [u.created_at, u.count] }
# => [[Mon, 10 Aug 2015 21:00:00 BRT -03:00, 186]]
Why the results differ if the records are the same? Why one result comes with timezone, as DateTime, and the other comes as Date only?
Is activerecord 'casting' to DateTime with Timezone because created_at is defined that way (btw, this makes the dates incorrect in this case)?
The timestamp isn't incorrect - that is, it's 2015-08-11 at midnight UTC - it's just displaying in your local time.
Rails has a bit of special behavior for created_at and updated_at:
The timestamps macro adds two columns, created_at and updated_at. These special columns are automatically managed by Active Record if they exist.
It always treats created_at coming back from a query as a timestamp. Your query returns just the date 2015-08-11, which is interpreted as midnight. When printed, the timestamp is displayed in your locale's timezone (which I presume must be -03:00), leading to 3 hours before midnight on the 11th.
When you name the result created_at_day, you avoid Rails converting it to a timestamp and get just the date you expect.
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 need to retrieve all rows from a table where the created_at timestamp is during a certain hour ... say 04:00 and 05:00. Anyone know how to do this?
RecordNameHere.find_by_sql("SELECT * FROM `table_name_here` WHERE HOUR(created_at) = HOUR('4:01:00')")
The MySQL documentation is awesome: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_hour
For multiple hour range (eg: records between in 4:00 to 6:00)
User.all(:conditions => "HOUR(created_at) BETWEEN ? AND ?", 4, 5)
For single hour use the following syntax:
User.all(:conditions => "HOUR(created_at) = ?", 4)
Note 1
The HOUR method returns the hour in 24 hour format. Provide the hour value accordingly.