In my Rails 5 app I want to send an alert 10 minutes after a certain datetime in my database. For example 10 minutes after the created_at datetime.
So I run a cronjob every 1 minute and have to look in my database for all records where created_at was 10 minutes ago.
My created_at field is in the format 2017-06-01 12:00:00. How can I get all records where created_at was 10 minutes ago?
My problem are the seconds and I can't simple do (this is not valid Ruby code, it's just to show my point):
where created_at == 10.minutes.ago
I would have to do something like
where created_at > 10.minutes.ago AND created_at < 9.minutes.ago
But this does not seem very clean. Are there better solutions?
You have to provide range in order to get records..however, to avoid dependencies with seconds, you can do something like below elegantly..
range = range = 10.minutes.ago.beginning_of_minute..10.minutes.ago.end_of_minute
records = YourModel.where(created_at: range)
Related
my table has 3 columns: data type timestamp,
|created_At | final_time| duracion(difference between created at and final_time)
| | |
the column difference should save the difference in hours and minutes, in this format HH:MM
this is my controller:
def horario
horario.update(duracion: params[:duracion]) // this params is "00:59"
end
but in the table Horarios, in column duracion i have this:
2017-12-24 03:59:00
so i want to save 00:59 (59 minutes) but postgres save all current date and add 3 hours more.
i want to save so in the future i will be able tu sum column duracion. Or should i change data type for this column? In this case which datatype you recomend me for rails to save HH:MM??
thanks.
Rails 5 supports PostgreSQL's interval type to some extent. You can create interval columns in the usual way and they will be properly represented in db/schema.rb. You can also assign them values in the usual way so you can say things like:
model.some_interval = '6 hours'
and get 06:00:00 inside the database. However, there is nothing in Ruby or Rails that properly represents a time interval (we only have various timestamp and date classes) so when that interval comes out of the database, you'll have a string on your hands, i.e:
> model = Model.find(some_id)
> model.some_interval.class
=> String
so you might end up having to manually parse some strings in Ruby. For simple intervals like '6 hours', this will be easy but it won't be so easy with more complicated intervals like '6 years 23 days 11 hours'.
If you'll only be working with your time intervals inside the database then interval would be natural and easy, you can say things like:
select some_timestamp + some_interval
and
where some_timestamp + some_interval < some_other_timestamp
and everything will work nicely.
However, if you need to work with the intervals back in Ruby then you'd probably be better off storing the interval as a number of seconds in an integer column (or whatever resolution you need). Then you could say things like:
where some_timestamp + (some_interval_in_seconds || 'seconds')::interval < some_other_timestamp
inside the database and
some_time + model.some_interval_in_seconds
back in Ruby.
In any case, strings are probably the wrong approach unless you really like parsing strings everywhere all the time.
As others already pointed out, Rails handles the Postgres Interval type as a string. A string that, unfortunately, is not easy to parse.
If you do this:
u = Users.select('edited_at - created_at as time_dif')
puts u.first['time_dif']
You can get something like 168 days 12:51:20.851115. Ugly right?
Well, using Ruby to convert this string into an useful number is not easy, but you can use Postgres to do the job for you. You will need to do a plain SQL query though, but it's the best method I've found so far:
query = ActiveRecord::Base.connection.execute("
SELECT EXTRACT(epoch FROM time_dif)/3600 as hours_dif
FROM
(
SELECT (edited_at - created_at) as time_dif
FROM users
) AS MainQuery
")
In this example, Postgres' EXTRACT function will convert the Interval type into a number which represents the total seconds of the interval. If you divide this number by 3600 you will get the different in hours as in the example above.
Then, if you want to iterate over the results:
query.each do |r|
puts r['hours_dif']
end
You could save duracion as a float type, where duracion would equal something like final_time - created_at and this value would be the difference in seconds. You can then perform arithmetic with these values and always convert back to minutes, hours, or whatever you need.
Hello guys I'm working on a interesting real time application.
The application is as follows.I have a meter model and meter_info model
calss Meter
has_many :meter_infos
# filed: id
end
class MeterInfo
belongs_to :meter
# field: meter_id, voltage
end
In every two minutes a new data is being saved to meter_info table.So you can imagine there are a huge data set over there.
Now what I want do is to find out exactly one voltage record of 10 meters each at a time in 10 minutes interval within 1 day.
So the result would be something like this
id created_at meter_id voltage
2001 2017-10-19 15:40:00 2 100
2001 2017-10-19 15:45:00 1 100
2001 2017-10-19 15:39:00 3 100
2001 2017-10-19 15:48:00 4 100
2001 2017-10-19 15:38:00 5 100
2001 2017-10-19 15:42:00 6 100
...
...
I've tried several queries but as it's taking too much time to find out the record, the request gets timeouted. Here is what I have tried for
(('2017-07-02 00:00:00').to_datetime.to_i ..
('2017-07-02 23:59:59').to_datetime.to_i).step(10.minutes) do |date|
query = "SELECT created_at, meter_id, voltage
FROM meter_infos
WHERE created_at between '#{Time.at(date).utc}' and
'#{Time.at(date).utc + 10.minutes}'
AND meter_id in (1,2,3,4,5)
ORDER BY id desc limit 1"
voltages = ActiveRecord::Base.connection.execute(query)
end
Which is timeouted even in the development environment.
Then I've tried to use Postgresql's generated_series like below
query= "SELECT meter_id,voltage, count(id) as ids
, GENERATE_SERIES( timestamp without time zone '2017-10-19',
timestamp without time zone '2017-10-19',
'10 min') as time_range
from meter_infos
where meter_infos.created_at between '2017-10-19 00:00:01'::timestamp and '2017-10-19 23:59:59'::timestamp
and meter_infos.meter_id in (1,2,3,4,5)
GROUP BY meter_id, voltage
ORDER BY meter_id ASC limit 1"
sbps_plot = ActiveRecord::Base.connection.execute(query)
Which is faster but gives me wrong data.
I am using Ruby on Rails and Postgresql.
Can somebody help me to write the faster query to find out data against time or suggest me any procedure to handle time series data analysis.
Thanks in advance.
You have records every two minutes, but you want to get a sample record from ten minute intervals. Here's my suggested solution:
You can take the modulus of the epoch time of the created_at timestamp with 600 (ten minutes in seconds). Then compare this against some 'tolerance' value (e.g. 119 seconds or less) in case the timestamps of your records aren't aligned to perfect ten minute intervals. Think of it of retrieving the first record with a created_at inside a 2 minute window following each 10 minute interval of the day.
For example,
MeterInfo
.where(
meter_id: [1, 2, 3, 4, 5],
created_at: your_date.beginning_of_day..your_date.end_of_day
)
.where("(cast(extract(epoch from created_at) as integer) % 600) < 119")
Give that a try and see if it works for you.
I'm having difficulty querying data by an hour for the previous day. Using Rails 4 on Postgres.
eg,
Table X:
created_at, value
timestamp1 3
timestamp2 5
I want to get:
time, value
YYMMDD 00:00 15
YYMMDD 01:00 20
basically the sum per hour. I've tried
Rails & Postgresql: how to group queries by hour?
Is it possible to group by hour/minute/quarter hour directly in ActiveRecord/Rails?
but I still cant figure out how to get it to work properly, i get the wrong hour for replies. How would i set my timezone along with this query?
TableX.group("DATE_trunc('hour',created_at)").count
Thanks!
All you need is create a query like this one:
SELECT date_trunc('hours', created_at), sum(value)
FROM TableX
GROUP BY date_trunc('hours', created_at)
This one uses date_trunc instead of suggested date_part, which fits your question better.
I have a DateTime in my at field and a number of minutes duration in my minutes field. I want to get all the records where the current time falls within at and at + minutes. Something like:
SpecialEvent.where(at: (DateTime.now - 120.minutes)..DateTime.now)
Except I'm guessing the 120.minutes duration. The exact number of minutes duration for each SpecialEvent is in a minutes field. But I don't know how to use that minutes field within the query...
Some databases (including MySQL) support functions in SQL to perform calculations with dates. This is required to efficiently calculate the timestamp based on data inside the database. Thus, you need to generate correct SQL for your specific database flavour. The example below should work for MySQL. If you use another database, you probably need to adapt this to the available date functions.
SpecialEvent.where(['at >= :now - INTERVAL minutes MINUTE AND at <= :now', now: DateTime.now])
This effectively generates the following SQL query (although with a literal date value instead of NOW():
SELECT * FROM special_events WHERE at >= NOW() - INTERVAL minutes MINUTE AND at <= NOW();
You can format your query like:
SpecialEvent.where(at: (DateTime.now - 120.minutes)..DateTime.now, minutes: '<your search value>')
I need to run a group_by query in Ruby on Rails, but I first want to adjust all records in the created_at column by a certain hour amount before running the query. So, for example, adding 9 hours to every record in the created_at field, and then grouping by date.
Something like the following (which is incorrect):
#foo = Bar.group("date(created_at + 9.hours)").count
How can I accomplish this in Rails?
PostgreSQL has excellent support for manipulating dates and times (see Date/Time Functions and Operators). You can express '9 hours' as an interval, add it to a timestamp, and cast to a date:
=> select (now()::timestamp + '9 hours'::interval)::date;
date
------------
2012-09-22
(1 row)
This ends up strikingly similar to your original pseudocode:
#foo = Bar.group("date(created_at + '9 hours'::interval)").count