Query for all rows with created_at between certain hours? - ruby-on-rails

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.

Related

How to get users that was created on specific day of week on ruby?

i'm trying to create some kpi's on ruby on rails.
How can i count all users that was created on sunday or on wednesday for example?
I now that ruby can get day of week with the follow code for example:
u = User.last
u.created_at.wday #==> wday get the day of week (0: sunday, 1: monday..)
But the following doesn't work:
User.where(created_at.wday: 0).count
And I don't want to loop each user, check if it was created on sunday and put it in an array, because it seems to be costly.
Any ideas?
If you're using MYSQL you can use dayofweek
User.where('DAYOFWEEK(created_at) = ?', 0).count
For Postgres this should do the trick:
User.where("extract(dow from created_at) = ?", 0)
EDIT: For SQLITE the following works:
User.where("strftime('%w', created_at) = ?", 0)

With Rails, how can I query the db for an aggregate number and grouped by week?

Given a table like so:
Log:
id, user_id, points, created_at
With Rails, how can I query the database by user_id and then GROUPED by week via created_at where I can then having something like:
WEEK of X/XX/XXXX - 3011 total points
WEEK of X/XX/XXXX - 320 total points
WEEK of X/XX/XXXX - 31 total points
WEEK of X/XX/XXXX - 30330 total points
Thanks
points_by_week = Log.where(user_id: user_id).group("DATE_TRUNC('year', created_at)", "DATE_TRUNC('week', created_at)").sum(:points)
Will yield a result like
{[2014, 4]=>3,
[2014, 8]=>7,
[2015, 4]=>26,
[2015, 6]=>19,
[2015, 12]=>50,
[2015, 32]=>48,
[2016, 2]=>78,
[2016, 3]=>45,
[2016, 4]=>49,
[2016, 5]=>110,
[2016, 45]=>30,
[2017, 4]=>130,
[2017, 11]=>185}
where the key is [year, week], then you can use Date.commercial to get the week of
points_by_week.each do |(year, week), count|
date = Date.commercial(year, week)
puts "Week of #{date.strftime('%d/%m/%Y')} - #{count} total points"
end
And (just cause I can), the mysql version of the query looks like this
points_by_week = Log.where(user_id: user_id).group("year(created_at)", "week(created_at)").sum(:points)
This by default assumes the week starts on Monday, and the first week of a year is starts on the first Monday of the year
something like:
points_by_week = Log.select("DATE_TRUNC('week', created_at) as week, sum(points) as total_points").group("week").where(user_id: x)
points_by_week.each do |pw|
puts "Week of #{pw.week.strftime('%d/%m/%Y')} - #{pw.total_points} total points"
end
EDIT
as per Michael Gorman comment the above won't distinguish between years (2017, 2016, etc), so you might need to group by week and year depending on your requirements:
Log.select("DATE_TRUNC('week', created_at) as week, DATE_TRUNC('year', created_at) as year, sum(points) as total_points").group("week, year")
and you can keep using the above loop to display it ;)
Something like this:
select 'WEEK of ' || TO_CHAR(date_trunc( 'week', created_at ), 'dd/mm/yyyy') || ' - ' || sum(points) || ' total points'
FROM clients
WHERE id < 100000
GROUP BY date_trunc( 'week', created_at );
|| is used to concatenate string.
TO_CHAR is used as a data type formatting functions.

ruby how to group by given date

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.

Average number of days since a date in postgresql/rails

I want to calculate the average number of days since 'date_from' (it varies)
User.all.average('? - date_from', Time.now.to_date)
Gives the error
undefined method `except' for Thu, 27 Nov 2014:Date
See Ruby_on_Rails/ActiveRecord/Calculations
The second parameter of average is options(an hash)
The options can be used to customize the query with :conditions, :order, :group, :having and :joins.
So you should generate average expression like this:
User.average("'#{Time.now.to_date}' - date_from" )
Then Rails will generate SQL like this:
SELECT AVG('2014-11-27' - date_from) AS avg_id FROM ...
You can try Date's ::today method, instead of Time's ::now:
User.all.average('? - date_from', Date.today)
How about:
#get difference fro all users from Time.now and date_from
dates = User.all.pluck(:date_from).map{|date| Time.now - date}
#sum all elements from array and divide with number of elements
#to get average
days = dates.reduce(0,:+)/dates.length
#round to 2 decimal places for better display
days = days.round(2)

Output array based on date (sales figures by day)

I have a table with a float called 'cost' and timestamp called'created_at'.
I would like it to output an array with the summing the costs for each particular day in the last month.
Something like:
#newarray = [] #creating the new array
month = Date.today.month # Current Month
year = Date.today.year # Current Year
counter = 1 # First Day of month
31.times do #for each day of the month (max 31)
#adding sales figures for that day
#newarray.push(Order.sum(:cost, :conditions => {:created_at => "#{year}-#{month}-#{counter}"}))
counter = counter + 1 #go onto next day
end
However this doesn't work as all the timestamps have a time as well.
Apologies in advance for the poor title, I can't seem to think of a sensible one.
You should be able to use code like the following:
sales_by_day = Order.sum(:cost,
:group => 'DATE(created_at)',
:conditions => ['DATE(created_at) > ?', 31.days.ago])
(0..30).collect { |d| sales_by_day[d.days.ago.to_date.to_s] || 0 }.reverse
This will sum the order cost by day, then create an array with index 0 being the last 24 hours. It will also take only one query, whereas your example would take 31.
To change the cut off date, replace 31.days.ago with an instance of the Time class. Documentation here: http://ruby-doc.org/core/classes/Time.html
Good luck!
This should work:
(Date.today.beginning_of_month..Date.today.end_of_month).map { |d|
Order.sum(:cost, :conditions => ['created_at >= ? AND created_at < ?', d, d+1])
}
Although I think you should try getting it using a single query, instead of making one for each day.

Resources