Find average/min/max of operation between database fields in Rails - ruby-on-rails

Relatively new to SQL and ORM.
Let's say I have a database table with start_at and finish_at fields (both datetime). Table contains 10000 items for example.
How to calculate the average, max/min duration (finish_at - start_at) using ruby or Active Record tools? There is no need to write it somewhere, just need numbers.

table = MyClass.arel_table
duration = table[:finish_at] - table[:start_at]
MyClass.pick(duration.maximum, duration.minimum, duration.average)
#=> ["125 days 20:46:34.05816", "00:00:00.063579", "20 days 23:30:16.221092"]
Cast them to another data type as needed or use directly in other queries.

Related

Where comparison on join table with buffer value on rails

How can I use the join table's column value with arithmetic operation during the where condition on Rails?
User and Order are the two Schema, Order has user via Foreign key relation
My goal is to find if an Order was created/placed within 5 minutes of User creation (Understanding Users who signup for placing an Order)
Tried the following queries
Order.where('country': 'US').joins(:user).where('orders.created_at <= :u_date', {u_date: 'users.created_at' + 5.minutes })
With this query we get the following error no implicit conversion of Time into String, so the users.created_at is not evaluating into a Date
Hence tried converting the string to DateTime objects, which failed too
Order.joins(:user).where('orders.created_at < ?', 'users.created_at'+ 5.minutes)
How can I do the comparison inside the Where query?
Right now I am plucking the data and comparing it, It'd be great to make it work inside the Where or any relevant query itself
You're invoking + on a string passing as argument a Time object, which is not an out-of-the-box operation, at least in Rails.
If the time to add is not dynamic you could try;
where("orders.created_at <= users.created_at + INTERVAL '5.minutes'")
which makes your DBMS add the proper interval to users.created_at (in this case I'm assuming Postgresql)

Getting Unix Time from Active Record ruby on rails

I have one table Bandwidth where I am saving user_traffic and created_at fields. I want to return user_name and created_at but I want date to be in Unix Time stamp in ruby on rails without using Query but by activerecord.
Bandwidth.all.select("user_traffic,created_at")
Above return both but in normal date format, but I want it to be in Unix
created_at: "2019-06-26 11:28:39", user_traffic: 0
I tried following and it works, but I could not get other column in this.
Bandwidth.find_by(id: 2).created_at.to_i
It return just timestamp which is perfect but how can I add other columns in this query. I think it is using model function to_i
=> 1561548975
If you don't need the relation later, you can use map to make any data format you want.
EX:
Bandwidth.all
.select(:user_traffic, :created_at)
.map{|b| [b.user_traffic, b.created_at.to_i]}

Reduce Queries in Rails 5

I need to retrieve a list of collections ie #medications, #treatments, #therapies etc. with a count of each collections related records.
This works but creates the initial query and then a new query for each related record count. Is there a way I can minimize the number of queries?
#medications = Medication.includes(:records).select(:id, :name).where(office_id: current_user.selected_office)
#medications.each do |medication|
medication.record_count = medication.records.count
end
if #medications query has 10 results I have total of 11 queries. I need 10 collections with related record counts so I would end up with 110 queries per request.
All models have same attributes of name, office_id , etc
I am wondering how I can restructure database to better use or restructure query. Incidentally I am using Postgres db v 9.6
What about with joins and count?:
Medication.left_outer_joins(:records)
.select('medications.name, medications.id, COUNT(records.id) AS records_count')
.group(:id)
.where(office_id: current_user.selected_office)
If you're planning to count on each record per medication, then you can use joins, and count passing the records and assigning an alias.
As each model has the name and id columns, you need to be more precise on defining them, and group in this case (correct me if I'm wrong), is mandatory, otherwise you'd get a PG::GroupingError.

Rails - Ruby enumerable select by date

In my Expense model I have a date attribute called payment_date. This is a Date format and not DateTime.
In one of my views Im displaying this data in a few different formats. and I want to avoid multiple queries.
For example, right next to Expense.all I need to display expenses year to date. Rather than running two queries to pull essentially the same information, I thought I would try to pluck the YTD data from #expenses = Expense.all.
Right now I'm trying to use:
#expenses.select { |ex| ex.payment_date > Date.today.beginning_of_year }
but this is returning a blank array.
Is it possible to select results by date, and where am i messing up?
To include Jan 1 of this year in your YTD expenses, use >= instead of > in your select block.
Since you tagged this with Rails, an even more performant way to query this is by using ActiveRecord/SQL.
If you have many records, doing #expenses = Expense.all and then using the Ruby enumerable select on that collection will load all of the expenses from the DB into memory. This could be quite slow, or could even cause out-of-memory errors!
You can do (assuming the DB is Postgres):
#ytd_expenses = Expense.where("payment_date >= ?", Date.today.beginning_of_year)
This will only return the results you care about from the DB.

RoR sum column of datetime type

I'm calculating total "walk time" for dog walking app. The Walks table has two cols, start_time and end_time. Since I want to display total time out for ALL walks for a particular dog, I should just be able to sum the two columns, subtract end_times_total from start_time_totals and result will be my total time out. However I'm getting strange results. When I sum the columns thusly,
start_times = dog.walks.sum('start_time')
end_times = dog.walks.sum('end_time')
BOTH start_times and end_times return the same value. Doing a sanity check I see that my start and end times in the db are indeed set as I would expect them to be (start times in the morning, end times in the afternoon), so the sum should definitely return a different value for each of the columns. Additionally, the value is different for each dog and in line with the relative values I would expect, so dogs with more walks return larger values than dogs with fewer walks. So, it looks like the sum is probably working, only somehow returning the same value for each column.
Btw, running this in dev Rails 3.2.3, ruby 2.0, SQLite.
Don't think that summing datetimes is a good idea. What you need is calculate duration of each single walk and sum them. You can do it in 2 ways:
1. DB-dependent, but more efficient:
# sqlite in dev and test modes
sql = "strftime('%s',end_time) - strftime('%s',start_time)" if !Rails.env.production?
# production with postgres
sql = "extract(epoch from end_time - start_time)" if Rails.env.production?
total = dog.walks.sum(sql)
2. DB-agnostic, but less efficient in case of hundreds record for each dog:
total = dog.walks.all.inject(0) {|tot,w| tot+=w.end_time-w.start_time}
I don't know, how sqlite handles datetime and operations on this data type, but while playing in sqlite console, I noticed that I could get reliable effects when converting datetime to seconds.
I would write it like:
dog.walks.sum("strftime('%s', end_time) - strftime('%s', start_time)")
Query should look like:
select sum(strftime('%s', end_time) - strftime('%s', start_time)) from walks;

Resources