Rails AR query between hour and minute - ruby-on-rails-3

Rails 3 app, pg adapter, my schema look as:
create_table "events", :force => true do |t|
t.integer "event_type_id"
t.datetime "start_at"
#...
end
Some example data include:
p.events.each{|e| puts e.start_at }; 1
2009-03-23 08:30:00 UTC
2009-05-20 07:45:00 UTC
2009-05-20 07:45:00 UTC
2009-03-23 16:00:00 UTC
2009-05-20 10:00:00 UTC
2009-03-23 19:30:00 UTC
2009-03-23 11:30:00 UTC
2009-05-20 07:45:00 UTC
2009-05-20 07:45:00 UTC
2009-05-20 09:00:00 UTC
2009-05-20 07:45:00 UTC
I'd like to search in between the next 3 hours, but ignoring the day! Hour and minute is all that matters to me..
Is it possible to be done in Postgres? I've been trying to use pg EXTRACT(), but no success so far
I don't want to filter all dates in ruby code :(

I introduce only one way how this can be done in PostgreSQL. I've no RoR experience so I can't help you on that part.
The basic idea below is that all timestamps are converted to the same day so the hours and minutes can be compared:
convert timestamps to strings that include only hours an minutes
converting that string back to a timestamp causes the date part to be 1st Jan at year 1
create table test (id serial, start_at timestamp);
insert into test(start_at)
select * from generate_series('2008-03-01 00:00'::timestamp,
'2008-03-15 12:00', '5 hours');
with
hh24mi_now as (select to_timestamp(to_char(current_timestamp, 'HH24:MI'), 'HH24:MI'))
select * from test where id in (
select id
from test
where to_timestamp(to_char(start_at, 'HH24:MI'), 'HH24:MI') >= (select * from hh24mi_now)
and to_timestamp(to_char(start_at, 'HH24:MI'), 'HH24:MI') < (select * from hh24mi_now) + interval '3 hours'
)
order by id
;
Result of an example run:
id | start_at
----+---------------------
13 | 2008-03-03 12:00:00
18 | 2008-03-04 13:00:00
23 | 2008-03-05 14:00:00
37 | 2008-03-08 12:00:00
42 | 2008-03-09 13:00:00
47 | 2008-03-10 14:00:00
61 | 2008-03-13 12:00:00
66 | 2008-03-14 13:00:00

Ignoring the minutes, and only taking into account the hours:
#On my model
scope :next_3_hours, ->(h){
where( [" date_part( 'hour', \"start_at\") = ? OR date_part( 'hour', \"start_at\") = ? OR date_part( 'hour', \"start_at\") = ? ",h,h+1,h+2])
.order("date_part( 'hour', \"start_at\") ASC, date_part( 'minute', \"start_at\")")
}
result:
# on SQL (considering now to be 17:00)
SELECT "events".* FROM "events"
WHERE ( date_part( 'hour', "start_at") = 17
OR date_part( 'hour', "start_at") = 18
OR date_part( 'hour', "start_at") = 19 )
ORDER BY date_part( 'hour', "start_at") ASC, date_part( 'minute', "start_at")

Related

Oracle sql create agenda

I have a table with interval dates and times. Can i create a full list with this data?
Table example:
Start_Date, End_Date, Start_Time, End_Time, Interval
01-jun-2021 02-jun-2021 08:00 10:00 30
03-jun-2021 04-jun-2021 10:00 12:00 15
Result:
01-jun-2021 08:00
01-jun-2021 08:30
01-jun-2021 09:00
01-jun-2021 09:30
02-jun-2021 08:00
02-jun-2021 08:30
02-jun-2021 09:00
02-jun-2021 09:30
03-jun-2021 10:00
03-jun-2021 10:15
03-jun-2021 10:30
03-jun-2021 11:00
03-jun-2021 11:15
03-jun-2021 11:30
03-jun-2021 11:45
04-jun-2021 10:00
04-jun-2021 10:15
04-jun-2021 10:30
04-jun-2021 11:00
04-jun-2021 11:15
04-jun-2021 11:30
04-jun-2021 11:45
Thanks.
This is a handy place to use a recursive CTE:
with cte (start_date, end_date, interval) as (
select to_date(start_date||start_time, 'DD-Mon-YYYYHH24:MI'), to_date(end_date||end_time, 'DD-Mon-YYYYHH24:MI'), interval
from t
union all
select cte.start_date + cte.interval * interval '1' minute, end_date, interval
from cte
where cte.start_date < end_date
)
select cast(start_date as timestamp)
from cte
order by start_date;
Here is a db<>fiddle.
You can use a recursive CTE, but the logic has to skip to the next day when you reach the end time; so this works:
with rcte (date_time, end_date, start_int, end_int, step_int) as (
select
start_date + to_dsinterval('0 ' || start_time || ':00'),
end_date,
to_dsinterval('0 ' || start_time || ':00'),
to_dsinterval('0 ' || end_time || ':00'),
interval * interval '1' minute
from your_table
union all
select
case
when date_time + step_int < trunc(date_time) + end_int
then date_time + step_int
else trunc(date_time) + interval '1' day + start_int
end,
end_date,
start_int,
end_int,
step_int
from rcte
where date_time + step_int < end_date + end_int
)
select date_time
from rcte
order by date_time
DATE_TIME
-------------------
2021-06-01 08:00:00
2021-06-01 08:30:00
2021-06-01 09:00:00
2021-06-01 09:30:00
2021-06-02 08:00:00
2021-06-02 08:30:00
2021-06-02 09:00:00
2021-06-02 09:30:00
2021-06-03 10:00:00
2021-06-03 10:15:00
2021-06-03 10:30:00
2021-06-03 10:45:00
2021-06-03 11:00:00
2021-06-03 11:15:00
2021-06-03 11:30:00
2021-06-03 11:45:00
2021-06-04 10:00:00
2021-06-04 10:15:00
2021-06-04 10:30:00
2021-06-04 10:45:00
2021-06-04 11:00:00
2021-06-04 11:15:00
2021-06-04 11:30:00
2021-06-04 11:45:00
db<>fiddle showing the anchor member including converting the times and interval to real day to second intervals types for later use; the anchor and recursive members with all the intermediate columns; and finally just this version with a single column.
You can format the resulting date value however you want, of course.

Oracle get last weekday Mon-Fri

I would like to obtain the last weekday.
If it's Tues to Sat, it will be the previous day. If it's Sunday or Monday, it will be Friday.
So far, I've tried this, but I'm struggling to get the desired output.
SELECT
level AS dow,
trunc(sysdate, 'D') + level day,
to_char(trunc(sysdate, 'D') + level, 'Day') AS day_week,
CASE
WHEN to_char(trunc(sysdate, 'D') + level, 'Day') IN (
'Sunday',
'Monday'
) THEN
trunc(sysdate - 2, 'IW') + 4
ELSE
sysdate - 1
END calculation
FROM
dual
CONNECT BY
level <= 7;
This solution works independent of language and territory:
SELECT date_value,
date_value - CASE TRUNC(date_value) - TRUNC(date_value, 'IW')
WHEN 0 THEN 3 -- Monday
WHEN 6 THEN 2 -- Sunday
ELSE 1 -- Tuesday to Saturday
END AS previous_weekday
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (date_value) AS
SELECT TRUNC(sysdate - LEVEL + 1)
FROM DUAL
CONNECT BY LEVEL <= 7;
Outputs (with the date format YYYY-MM-DD HH24:MI:SS (DY)):
DATE_VALUE
PREVIOUS_WEEKDAY
2021-07-20 00:00:00 (TUE)
2021-07-19 00:00:00 (MON)
2021-07-19 00:00:00 (MON)
2021-07-16 00:00:00 (FRI)
2021-07-18 00:00:00 (SUN)
2021-07-16 00:00:00 (FRI)
2021-07-17 00:00:00 (SAT)
2021-07-16 00:00:00 (FRI)
2021-07-16 00:00:00 (FRI)
2021-07-15 00:00:00 (THU)
2021-07-15 00:00:00 (THU)
2021-07-14 00:00:00 (WED)
2021-07-14 00:00:00 (WED)
2021-07-13 00:00:00 (TUE)
db<>fiddle here

compare oracle row count between different dates hourly

I am using this sql to query the count of rows hourly for three days ago ...
select trunc(sendtime ,'hh24') , count(*)
FROM t_sendedmsglog
where msgcontext like '%sm_%_tone_succ%' and sendtime > sysdate -3
group by trunc(sendtime ,'hh24')
order by trunc(sendtime ,'hh24') desc;
and the result shows like :
for example:
#|TRUNC(SENDTIME,'HH24')|COUNT(*)|
1|10/15/2020|12:00:00 PM|593|
2|10/15/2020|11:00:00 AM|889|
3|10/15/2020|10:00:00 AM|854|
4|10/15/2020|9:00:00 AM|1027|
5|10/15/2020|8:00:00 AM|8409|
.
.
.
12|10/15/2020|1:00:00 AM|101|
13|10/15/2020|281|
14|10/14/2020|11:00:00 PM|722|
15|10/14/2020|10:00:00 PM|1381|
16|10/14/2020|9:00:00 PM|2123|
.
.
25|10/14/2020|12:00:00 PM|1195|
26|10/14/2020|11:00:00 AM|1699|
27|10/14/2020|10:00:00 AM|747|
28|10/14/2020|9:00:00 AM|827|
.
.
40|10/13/2020|9:00:00 PM|2058|
41|10/13/2020|8:00:00 PM|2800|
but how I can make the result appear like below instead, so I can compare the count between different days for the same hour ?
hour|10/12/2020|10/13/2020|10/14/2020|count(*)
11:00:00 PM|618 |509 |722 |
10:00:00 PM|3181|1144|1381|
09:00:00 PM|3520|2058|2123|
08:00:00 PM|3688|2800|9347|
07:00:00 PM|3648|3166|3469|
06:00:00 PM|3628|2973|4518|
05:00:00 PM|3644|2429|3607|
04:00:00 PM|3652|3678|2291|
03:00:00 PM|1017|7711|819 |
02:00:00 PM|814 |7693|1310|
01:00:00 PM|856 |825 |848 |
12:00:00 PM|558 |1531|1195|
11:00:00 AM|0 |1132|1699|
10:00:00 AM|0 |732 |747 |
09:00:00 AM|0 |709 |827 |
08:00:00 AM|0 |1256|947 |
07:00:00 AM|0 |1465|1502|
06:00:00 AM|0 |749 |780 |
05:00:00 AM|0 |181 |169 |
04:00:00 AM|0 |46 |32 |
03:00:00 AM|0 |23 |34 |
02:00:00 AM|0 |46 |39 |
01:00:00 AM|0 |82 |81 |
00:00:00 AM|0 | |218 |
Use conditional aggregation:
select trunc(sendtime, 'hh24') , count(*) as total,
sum(case when trunc(sendtime) = trunc(sysdate) - interval '2' day then 1 else 0 end) as yester2day,
sum(case when trunc(sendtime) = trunc(sysdate) - interval '1' day then 1 else 0 end) as yesterday,
sum(case when trunc(sendtime) = trunc(sysdate) - interval '0' day then 1 else 0 end) as today
from t_sendedmsglog
where msgcontext like '%sm_%_tone_succ%' and
sendtime >= trunc(sysdate) - interval '2' day
group by trunc(sendtime, 'hh24')
order by trunc(sendtime, 'hh24') desc;
Note that I tweaked the date comparison in the where clause as well. In Oracle, sysdate has a time component, which you don't care about for the filtering purposes.

How to generate series of 24hrs with 1 hour interval and display the last as 23:59:59

Project: BIRT
Datasource: Amazon Redshift
I want to generate a Data Set with value of:
00:00:00
1:00:00
2:00:00
3:00:00
4:00:00
5:00:00
6:00:00
7:00:00
8:00:00
9:00:00
10:00:00
11:00:00
12:00:00
13:00:00
14:00:00
15:00:00
16:00:00
17:00:00
18:00:00
19:00:00
20:00:00
21:00:00
22:00:00
23:00:00
23:59:59 //the last value should display like this
I was able to generate a series of 24hours with 1 hr interval, but I need to make the last one's value as 23:59:59
Query to generate 24 hours with 1 hour interval:
SELECT start_date + gs * interval '1 hour' as times
FROM (
SELECT '2019-05-21 00:00:00'::timestamp as start_date, generate_series(1,24, 1) as gs)
How is that?
Thanks
Updating your query, just adding a if for the last hour:
SELECT
start_date + gs * interval '1 hour'
- if(gs=24, interval '1 second', interval '0 second') as times
FROM (
SELECT
'2019-05-21 00:00:00'::timestamp as start_date
, generate_series(1,24, 1) as gs
)
I think too much about this, the simplest way to achieve this is just add a default value on the report parameter , if you're going to use the data set in the report parameter
or with this:
SELECT start_date + gs * interval '1 hour' as times
FROM (
SELECT '2020-01-01 00:00:00'::timestamp as start_date, generate_series(1,24, 1) as gs)
union
select '2020-01-01 23:59:59'::timestamp as start_date

SQL - move date to within 48 hr window

I have a bunch of historic timestamp dates. Basically, I need to simulate a new date such that the historic dates are moved to within a 48 hour window of the current date.
This is an extract of the date column:
2019-05-07 17:46:57.733 UTC
2019-05-15 13:03:25.247 UTC
2019-05-07 13:27:49.453 UTC
2019-05-11 04:24:02.293 UTC
2019-04-18 08:00:54.660 UTC
2019-04-25 05:34:36.777 UTC
2019-05-14 16:48:07.863 UTC
Assuming the current date is 2019-10-03 15:00:00. The expected range of dates should be between 2019-10-03 15:00:00 and 2019-10-01 15:00:00
The expected results should be the following.
2019-10-02 17:46:57.733 UTC
2019-10-03 13:03:25.247 UTC
2019-10-03 13:27:49.453 UTC
2019-10-03 04:24:02.293 UTC
2019-10-02 08:00:54.660 UTC
2019-10-02 05:34:36.777 UTC
2019-10-01 16:48:07.863 UTC
Why not just construct two days of random timestamps?
select timestamp_add(current_timestamp, interval cast(rand() * (60 * 60 * 24 * 2) as int64) second)
from t
It feels like you are looking for a random date function.
CREATE TEMP FUNCTION random_date()
RETURNS DATE
AS ( DATE_SUB(CURRENT_DATE(), INTERVAL CAST(FLOOR(RAND() * 29 / 10) AS INT64) DAY));
with data as (
select "2019-05-07 17:46:57.733 UTC" as date_time UNION ALL
select "2019-05-15 13:03:25.247 UTC" UNION ALL
select "2019-05-07 13:27:49.453 UTC" UNION ALL
select "2019-05-11 04:24:02.293 UTC" UNION ALL
select "2019-04-18 08:00:54.660 UTC" UNION ALL
select "2019-04-25 05:34:36.777 UTC" UNION ALL
select "2019-05-14 16:48:07.863 UTC" )
SELECT
CONCAT(FORMAT_DATE("%Y-%m-%d", random_date()), " ", SUBSTR(date_time, 12))
FROM data;
Output:
+-----------------------------+
| f0_ |
+-----------------------------+
| 2019-10-01 17:46:57.733 UTC |
| 2019-10-01 13:03:25.247 UTC |
| 2019-10-02 13:27:49.453 UTC |
| 2019-10-03 04:24:02.293 UTC |
| 2019-10-03 08:00:54.660 UTC |
| 2019-10-03 05:34:36.777 UTC |
| 2019-10-02 16:48:07.863 UTC |
+-----------------------------+