How to generate series for date range with minutes interval in oracle? - sql

In Postgres below query is working using generate_series function
SELECT dates
FROM generate_series(CAST('2019-03-01' as TIMESTAMP), CAST('2019-04-01' as TIMESTAMP), interval '30 mins') AS dates
Below query is also working in Oracle but only for date interval
select to_date('2019-03-01','YYYY-MM-DD') + rownum -1 as dates
from all_objects
where rownum <= to_date('2019-03-06','YYYY-MM-DD')-to_date('2019-03-01','YYYY-MM-DD')+1
SELECT dates
FROM generate_series(CAST('2019-03-01' as TIMESTAMP), CAST('2019-04-01' as TIMESTAMP), interval '30 mins') AS dates
I want same result in Oracle for below query
SELECT dates
FROM generate_series(CAST('2019-03-01' as TIMESTAMP), CAST('2019-04-01' as TIMESTAMP), interval '30 mins') AS dates

Use a hierarchical query:
SELECT DATE '2019-03-01' + ( LEVEL - 1 ) * INTERVAL '30' MINUTE AS dates
FROM DUAL
CONNECT BY DATE '2019-03-01' + ( LEVEL - 1 ) * INTERVAL '30' MINUTE <= DATE '2019-04-01';
Output:
| DATES |
| :------------------ |
| 2019-03-01 00:00:00 |
| 2019-03-01 00:30:00 |
| 2019-03-01 01:00:00 |
| 2019-03-01 01:30:00 |
| 2019-03-01 02:00:00 |
| 2019-03-01 02:30:00 |
| 2019-03-01 03:00:00 |
| 2019-03-01 03:30:00 |
| 2019-03-01 04:00:00 |
| 2019-03-01 04:30:00 |
| 2019-03-01 05:00:00 |
| 2019-03-01 05:30:00 |
...
| 2019-03-31 19:30:00 |
| 2019-03-31 20:00:00 |
| 2019-03-31 20:30:00 |
| 2019-03-31 21:00:00 |
| 2019-03-31 21:30:00 |
| 2019-03-31 22:00:00 |
| 2019-03-31 22:30:00 |
| 2019-03-31 23:00:00 |
| 2019-03-31 23:30:00 |
| 2019-04-01 00:00:00 |
db<>fiddle here

Related

Create a time table (hours by hours) in BigQuery?

How can I generate the following table in BigQuery:
+---------------------+
| mydate |
+---------------------+
| 2010-01-01 00:00:00 |
| 2010-01-01 01:00:00 |
| 2010-01-01 02:00:00 |
| 2010-01-01 03:00:00 |
| 2010-01-01 04:00:00 |
| 2010-01-01 05:00:00 |
+---------------------+
Use below
select ts
from unnest(generate_timestamp_array('2010-01-01 00:00:00', '2010-01-01 05:00:00', interval 1 hour)) ts
with output
Another option (based on #Daniel's comment and #Khilesh's answer)
select timestamp('2010-01-01 00:00:00') + make_interval(hour => hours_to_add)
from unnest(generate_array(0,5)) AS hours_to_add
obviously with same output as above
You can try this as well
SELECT
TIMESTAMP_ADD(TIMESTAMP("2010-01-01 00:00:00", INTERVAL hours_to_add HOURS) as mydate
from
(SELECT num1 as hours_to_add FROM UNNEST(GENERATE_ARRAY(0,2400)) AS num1)
Output :
+---------------------+
| mydate |
+---------------------+
| 2010-01-01 00:00:00 |
| 2010-01-01 01:00:00 |
| 2010-01-01 02:00:00 |
| 2010-01-01 03:00:00 |
| 2010-01-01 04:00:00 |
| 2010-01-01 05:00:00 |
+---------------------+

How to compare current row with previous column next row in sql

Date from Date to
2018-12-11 2019-01-08
2019-01-08 2019-02-09
2019-02-10 2019-03-14
2019-03-17 2019-04-11
2019-04-15 2019-05-16
2019-05-16 2019-06-13
output will be like this
Date from Date to Days
2018-12-11 2019-01-08 0
2019-01-08 2019-02-09 1
2019-02-10 2019-03-14 3
2019-03-17 2019-04-11 4
2019-04-15 2019-05-16 0
2019-05-16 2019-06-13 -
To return the difference between two date values in days you could use the DATEDIFF() Function, something like:
SELECT DATEDIFF(DAY, DayFrom, DayTo) AS 'DaysBetween'
FROM DateTable
You want lead() and a date diff function:
select
date_from,
date_to,
datediff(day, date_to, lead(date_from) over(order by date_from)) days
from mytable
datediff() is a SQLServer function. There are equivalents in other RDBMS.
Side note: I would recommend againts using a string value (-) for records that do not have a next record, since other values are numeric (the datatypes in a column must be consistant). null is good enough for this (which the above query will produce).
Demo on DB Fiddle:
date_from | date_to | days
:------------------ | :------------------ | ---:
11/12/2018 00:00:00 | 08/01/2019 00:00:00 | 0
08/01/2019 00:00:00 | 09/02/2019 00:00:00 | 1
10/02/2019 00:00:00 | 14/03/2019 00:00:00 | 3
17/03/2019 00:00:00 | 11/04/2019 00:00:00 | 4
15/04/2019 00:00:00 | 16/05/2019 00:00:00 | 0
16/05/2019 00:00:00 | 13/06/2019 00:00:00 | null

Splitting interval overlapping more days in PostgreSQL

I have a PostgreSQL table containing start timestamp and duration time.
timestamp | interval
------------------------------
2018-01-01 15:00:00 | 06:00:00
2018-01-02 23:00:00 | 04:00:00
2018-01-04 09:00:00 | 2 days 16 hours
What I would like is to have the interval splitted into every day like this:
timestamp | interval
------------------------------
2018-01-01 15:00:00 | 06:00:00
2018-01-02 23:00:00 | 01:00:00
2018-01-03 00:00:00 | 03:00:00
2018-01-04 09:00:00 | 15:00:00
2018-01-05 00:00:00 | 24:00:00
2018-01-06 00:00:00 | 24:00:00
2018-01-07 00:00:00 | 01:00:00
I am playing with generate_series(), width_bucket(), range functions, but I still can't find plausible solution. Is there any existing or working solution?
not sure about all edge cases, but this seems working:
t=# with c as (select *,min(t) over (), max(t+i) over (), tsrange(date_trunc('day',t),t+i) tr from t)
, mid as (
select distinct t,i,g,tr
, case when g < t then t else g end tt
from c
right outer join (select generate_series(date_trunc('day',min),date_trunc('day',max),'1 day') g from c) e on g <# tr order by 3,1
)
select
tt
, i
, case when tt+'1 day' > upper(tr) and t < g then upper(tr)::time::interval when upper(tr) - lower(tr) < '1 day' then i else g+'1 day' - tt end
from mid
order by tt;
tt | i | case
---------------------+-----------------+----------
2018-01-01 15:00:00 | 06:00:00 | 06:00:00
2018-01-02 23:00:00 | 04:00:00 | 01:00:00
2018-01-03 00:00:00 | 04:00:00 | 03:00:00
2018-01-04 09:00:00 | 2 days 16:00:00 | 15:00:00
2018-01-05 00:00:00 | 2 days 16:00:00 | 1 day
2018-01-06 00:00:00 | 2 days 16:00:00 | 1 day
2018-01-07 00:00:00 | 2 days 16:00:00 | 01:00:00
(7 rows)
also please mind that timestamp without time zone can fail you when comparing timestamps...

Get current week in postgreSQL

I have been searching the web for the proper postgreSQL syntax for current_week. I searched through the link attached but could not get anything fruition out of it Date/Time. My task is to get Sunday as the start of the week.
I tried same as current_date but it failed:
select current_week
There has to be a current week syntax for postgreSQL.
knowing that for extract('dow' from
The day of the week as Sunday (0) to Saturday (6)
and
By definition, ISO weeks start on Mondays
You can workaround by substracting one day:
select date_trunc('week', current_date) - interval '1 day' as current_week
current_week
------------------------
2016-12-18 00:00:00+00
(1 row)
Here is sample:
t=# with d as (select generate_series('2016-12-11','2016-12-28','1 day'::interval) t)
select date_trunc('week', d.t)::date - interval '1 day' as current_week, extract('dow' from d.t), d.t from d
;
current_week | date_part | t
---------------------+-----------+------------------------
2016-12-04 00:00:00 | 0 | 2016-12-11 00:00:00+00
2016-12-11 00:00:00 | 1 | 2016-12-12 00:00:00+00
2016-12-11 00:00:00 | 2 | 2016-12-13 00:00:00+00
2016-12-11 00:00:00 | 3 | 2016-12-14 00:00:00+00
2016-12-11 00:00:00 | 4 | 2016-12-15 00:00:00+00
2016-12-11 00:00:00 | 5 | 2016-12-16 00:00:00+00
2016-12-11 00:00:00 | 6 | 2016-12-17 00:00:00+00
2016-12-11 00:00:00 | 0 | 2016-12-18 00:00:00+00
2016-12-18 00:00:00 | 1 | 2016-12-19 00:00:00+00
2016-12-18 00:00:00 | 2 | 2016-12-20 00:00:00+00
2016-12-18 00:00:00 | 3 | 2016-12-21 00:00:00+00
2016-12-18 00:00:00 | 4 | 2016-12-22 00:00:00+00
2016-12-18 00:00:00 | 5 | 2016-12-23 00:00:00+00
2016-12-18 00:00:00 | 6 | 2016-12-24 00:00:00+00
2016-12-18 00:00:00 | 0 | 2016-12-25 00:00:00+00
2016-12-25 00:00:00 | 1 | 2016-12-26 00:00:00+00
2016-12-25 00:00:00 | 2 | 2016-12-27 00:00:00+00
2016-12-25 00:00:00 | 3 | 2016-12-28 00:00:00+00
(18 rows)
Time: 0.483 ms
One method would be date_trunc():
select date_trunc('week', current_date) as current_week

Group by an individual timeframe

I would like to group rows of a table by an individual time frame.
As an example let's imagine we have a list of departures at an airport:
| Departure | Flight | Destination |
| 2016-06-01 10:12:00 | LH1234 | New York |
| 2016-06-02 14:23:00 | LH1235 | Berlin |
| 2016-06-02 14:30:00 | LH1236 | Tokio |
| 2016-06-03 18:45:00 | LH1237 | Belgrad |
| 2016-06-04 04:10:00 | LH1237 | Rio |
| 2016-06-04 06:20:00 | LH1237 | Paris |
I can easily group the data by full hours (days, weeks, ...) using the following query:
select to_char(departure, 'HH24') as "full hour", count(*) as "number flights"
from departures
group by to_char(departure, 'HH24')
This should result in the following table.
| full hour | number flights |
| 04 | 1 |
| 06 | 1 |
| 10 | 1 |
| 14 | 2 |
| 18 | 1 |
Now my question: Is there an elegant way (or best practise) to group data by an individual time frame.
The result I'm looking for is the following:
| time frame | number flights |
| 2016-05-31 22:00 - 2016-06-01 06:00 | 0 |
| 2016-06-01 06:00 - 2016-06-01 14:00 | 1 |
| 2016-06-01 14:00 - 2016-06-01 22:00 | 0 |
| 2016-06-01 22:00 - 2016-06-02 06:00 | 0 |
| 2016-06-02 06:00 - 2016-06-02 14:00 | 0 |
| 2016-06-02 14:00 - 2016-06-02 22:00 | 2 |
| 2016-06-02 22:00 - 2016-06-03 06:00 | 0 |
| 2016-06-03 06:00 - 2016-06-03 14:00 | 0 |
| 2016-06-03 14:00 - 2016-06-03 22:00 | 1 |
| 2016-06-03 22:00 - 2016-06-04 06:00 | 1 |
| 2016-06-04 06:00 - 2016-06-04 14:00 | 1 |
| 2016-06-04 14:00 - 2016-06-04 22:00 | 0 |
| 2016-06-04 22:00 - 2016-06-05 06:00 | 0 |
(The rows with 0 flights aren't relevant. They are just there for a better visualization of the problem.)
Thanks for your answers in advance. :-)
Peter
Since you have groups starting at 22:00 and multiples of 8 hours afterwards then you can use TRUNC() and an offset of 2 hours to get the results grouped by each day.
You can then work out the which third of the day the departure is in and also group by that:
GROUP BY TRUNC( Departure + 2/24 ),
FLOOR( ( Departure + 2/24 - TRUNC( Departure + 2/24 ) ) * 3 )
Something like this should work. Please note the two input variables, first_time and timespan. The timespan is whatever you want it to be (I wrote it in the form 8/24 for eight hours; if you make timespan into a bind variable as a number expressed in HOURS, you need the division by 24). Due to the way I wrote the formulas, there are NO requirements on first_time other than it should be one of your boundary date/times; it may even be in the future, it won't change the results. It may also be made into a bind variable, then you can decide in what format you want it to be made available to the query.
with timetable (departure, flight, destination) as (
select to_date('2016-06-01 10:12:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1234', 'New York'
from dual union all
select to_date('2016-06-02 14:23:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1235', 'Berlin'
from dual union all
select to_date('2016-06-02 14:30:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1236', 'Tokyo'
from dual union all
select to_date('2016-06-03 18:45:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1237', 'Belgrad'
from dual union all
select to_date('2016-06-04 04:10:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1237', 'Rio'
from dual union all
select to_date('2016-06-04 06:20:00', 'yyyy-mm-dd hh24:mi:ss'), 'LH1237', 'Paris'
from dual
),
input_values (first_time, timespan) as (
select to_date('2010-01-01 06:00:00', 'yyyy-mm-dd hh24:mi:ss'), 8/24 from dual
),
prep (adj_departure, flight, destination) as (
select first_time + timespan * floor((departure - first_time) / timespan),
flight, destination
from timetable, input_values
)
select to_char(adj_departure, 'yyyy-mm-dd hh24:mi:ss') || ' - ' ||
to_char(adj_departure + timespan, 'yyyy-mm-dd hh24:mi:ss') as time_interval,
count(*) as ct
from prep, input_values
group by adj_departure, timespan
order by adj_departure
;
Output:
TIME_INTERVAL CT
----------------------------------------- ----------
2016-06-01 06:00:00 - 2016-06-01 14:00:00 1
2016-06-02 14:00:00 - 2016-06-02 22:00:00 2
2016-06-03 14:00:00 - 2016-06-03 22:00:00 1
2016-06-03 22:00:00 - 2016-06-04 06:00:00 1
2016-06-04 06:00:00 - 2016-06-04 14:00:00 1