I'm using following select statement to generate random data for one year
SELECT a intime
,a + format('%s', b || ' minutes')::interval otime
,b duration
FROM generate_series('2016-01-01 07:00:00'::timestamp, '2016-12-31 16:00:00', '05 minutes') s(a)
,generate_series(5, 20, 5) t(b)
where a::time between '07:00:00'
and '16:00:00';
This will produce 436 rows for each day in a month but how can I limit it to 30 rows in each day from 2016-01-01 to 2016-12-31
If you want 30 equal spaced times during a day:
SELECT s.a
FROM generate_series('2016-01-01'::timestamp, '2016-12-31',
'48 minutes') s(a)
Related
I'm trying to generate 2 series of timestamps with 30 minute interval like so:
interval_start,interval_end
2023-01-30 05:30:00.000000 +00:00,2023-01-30 06:00:00.000000 +00:00
2023-01-30 05:00:00.000000 +00:00,2023-01-30 05:30:00.000000 +00:00
2023-01-30 04:30:00.000000 +00:00,2023-01-30 05:00:00.000000 +00:00
I can generate each series but cannot combine them:
select *
from unnest(GENERATE_TIMESTAMP_ARRAY('2020-01-01', '2021-01-01', interval 30 minute)) start_times
select *
from unnest(GENERATE_TIMESTAMP_ARRAY(TIMESTAMP_ADD('2020-01-01', interval 30 MINUTE), '2021-01-01', interval 30 minute)) end_times
Consider below:
WITH intervals AS (
select *
from unnest(GENERATE_TIMESTAMP_ARRAY('2020-01-01', '2021-01-01', interval 30 minute)) interval_start
)
SELECT
interval_start, TIMESTAMP_ADD(interval_start, interval 30 minute) interval_end
FROM intervals
Output:
Good day everyone. I have a table as below. Duration is the time from current state to next state.
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
35
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
How do I split data at 9:00 AM of each day like below:
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
10
10/9/2022 9:00:00 AM
A
25
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
Thank you.
Use a row-generator function to generate extra rows when the timestamp is before 09:00 and the next timestamp is after 09:00 (and calculate the diff value rather than storing it in the table):
SELECT l.ts AS timestamp,
t.state,
ROUND((l.next_ts - l.ts) * 24 * 60, 2) As diff
FROM (
SELECT timestamp,
LEAD(timestamp) OVER (ORDER BY timestamp) AS next_timestamp,
state
FROM table_name
) t
CROSS APPLY (
SELECT GREATEST(
t.timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1
) AS ts,
LEAST(
t.next_timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL
) AS next_ts
FROM DUAL
CONNECT BY
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1 < t.next_timestamp
) l;
Which, for your sample data:
CREATE TABLE table_name (Timestamp, State) AS
SELECT DATE '2022-10-09' + INTERVAL '08:50' HOUR TO MINUTE, 'A' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:25' HOUR TO MINUTE, 'B' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:35' HOUR TO MINUTE, 'C' FROM DUAL UNION ALL
SELECT DATE '2022-10-12' + INTERVAL '09:35' HOUR TO MINUTE, 'D' FROM DUAL;
Outputs:
TIMESTAMP
STATE
DIFF
2022-10-09 08:50:00
A
10
2022-10-09 09:00:00
A
25
2022-10-09 09:25:00
B
10
2022-10-09 09:35:00
C
1405
2022-10-10 09:00:00
C
1440
2022-10-11 09:00:00
C
1440
2022-10-12 09:00:00
C
35
2022-10-12 09:35:00
D
null
fiddle
I need to select all dates, from current date, with a 30 days jump between each other in the past.
In example, today is October 18th, so I would need to get September 18th , August 19th, July 20th.
I cannot simply write the function current_date - 30, current_date - 60, current_date - 90 because the beginning of the data is far.
If you are using Oracle you can use below code -
WITH DATES(CUR_DATE, MON) AS (SELECT CURRENT_DATE CUR_DATE, 1 MON FROM DUAL
UNION ALL
SELECT D.CUR_DATE - 30, D.MON + 1
FROM DATES D
WHERE TO_CHAR(D.CUR_DATE, 'MM') > 1
AND TO_CHAR(D.CUR_DATE, 'YY') = TO_CHAR(SYSDATE, 'YY'))
SELECT * FROM DATES
Here is the fiddle. For other DBs please post your DBMS product.
If you are using Postgres this is quite easy using generate_series()
select d::date as "date"
from generate_series(current_date,
current_date - interval '1 year',
interval '-1 month') as t(d)
That returns the "last 12 months" starting from today.
If today is 2019-10-18, this returns:
date
----------
2019-10-18
2019-09-18
2019-08-18
2019-07-18
2019-06-18
2019-05-18
2019-04-18
2019-03-18
2019-02-18
2019-01-18
2018-12-18
2018-11-18
2018-10-18
If you want 30 days intervals (rather than 1 month), use:
select d::date as date
from generate_series(current_date,
current_date - interval '1 year',
interval '-30 day') as t(d)
For SQL Server, you can use the code below:
;WITH cte_Date30Days(n, d, dte)
AS (
SELECT 0, 0, current_timestamp as dte
UNION ALL
SELECT n+1, d - 30, dateadd(day, -30, dte)
FROM
cte_Date30Days
WHERE n < 7-1
)
SELECT dte FROM cte_Date30Days;
Only 7 rows are returned but change that value to suit your needs.
So, my aim is to be able to count time spent on certain activities in hour ranges.
My data contains: start of the certain activity and end of that activity,
for example I know that someone had break from '2019-01-09 17:04:34' to '2019-01-09 19:55:03'.
My aim is to calculate that this person spent 55 minutes on break in interval '17-18', 60 minutes on '18-19' and 55 minutes on '19-20'.
My idea was to always split the source so for the row containing start and and of the activity I would receive as many rows as my time range split in the hour ranges (for this sample data I would receive 3: rows with '2019-01-09 17:04:34' to '2019-01-09 17:59:59', '2019-01-09 18:00:00' to '2019-01-09 18:59:59' and '2019-01-09 19:00:00' to '2019-01-09 19:55:03')
If I could obtain something like that I could manage to count all things I need to. I predict that to obtain this result I should use CTE (as we don't know in how many ranges we need to split time interval), but I have no experience in it.
Hopefully I managed to explain my problem clearly. I work on oracle sql developer.
I'd be very grateful for your help on at least some tips.
Since you mentioned recursion, this uses recursive subquery factoring:
-- CTE for sample data
with your_table (id, start_time, end_time) as (
select 1, timestamp '2019-01-09 17:04:34', timestamp '2019-01-09 19:55:03' from dual
union all
select 2, timestamp '2019-01-09 23:47:01', timestamp '2019-01-10 02:05:03' from dual
union all
select 3, timestamp '2019-01-09 18:01:01', timestamp '2019-01-09 18:02:07' from dual
union all
select 4, timestamp '2019-01-09 13:00:00', timestamp '2019-01-09 14:00:01' from dual
),
-- recursive CTE
rcte (id, hour_period, minutes, period_start_time, end_time, hour_num) as (
select id,
-- first period is the original start hour
extract(hour from start_time),
-- minutes in first period, which can end at the end of that hour, or at original
-- end time if earlier
case when extract(minute from end_time) = 0
and end_time >= cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour
then 60
else extract(minute from
least(cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour, end_time)
- start_time
)
end,
-- calculate next period start
cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour,
-- original end time
end_time,
-- first hour period (for later ordering)
1
from your_table
union all
select id,
-- this period's hour value
extract(hour from period_start_time),
-- minutes in this period - either 60 if we haven't reach the end time yet;
-- or if we have then the number of minutes from the end time
case when end_time < period_start_time + interval '1' hour
then extract(minute from end_time)
else 60
end,
-- calculate next period start
period_start_time + interval '1' hour,
-- original end time
end_time,
-- increment hour period (for later ordering)
hour_num + 1
from rcte
where period_start_time < end_time
)
select id, hour_period, minutes
from rcte
order by id, hour_num;
ID HOUR_PERIOD MINUTES
---------- ----------- ----------
1 17 55
1 18 60
1 19 55
2 23 12
2 0 60
2 1 60
2 2 5
3 18 1
4 13 60
4 14 0
It find finds the amount of time spent in the first hour of the period in the anchor member, then recursively looks at subsequent hours until the end time is reached, increasing the passed-on period end time each time; and in the recursive member it checks whether to use a fixed 60 minutes (if it knows the end time hasn't been reached) or use the actual minutes from the end time.
My example periods include ones that span midnight, cover less than an hour, and that start in the first minute of an hour - and which end in the first minute of an hour, which (in my calculation anyway) ends up with a row for that hour anyway and the number of minutes as zero. You can easily filter that out if you don't want to see it.
It is not entirely clear from your post how you want to handle non-zero seconds components (what combination of rounding and/or truncation). In any case, that can be coded easily, once a complete set of non-contradictory rules is agreed upon.
Other than that, your question consists of two parts: identify the proper hours for each id (each activity or event), and the duration of the part of that event during that hour. In the query below, using the CONNECT BY hierarchical technique, I generate the hours and the duration as an interval day to second. As I said, that can be converted to minutes (between 0 and 60) once you clarify the rounding rules.
with
your_table (id, start_time, end_time) as (
select 1, timestamp '2019-01-09 17:04:34', timestamp '2019-01-09 19:55:03'
from dual union all
select 2, timestamp '2019-01-09 23:47:01', timestamp '2019-01-10 02:05:03'
from dual union all
select 3, timestamp '2019-01-09 18:01:01', timestamp '2019-01-09 18:02:07'
from dual union all
select 4, timestamp '2019-01-09 13:00:00', timestamp '2019-01-09 14:00:01'
from dual
)
select id,
trunc(start_time, 'hh') + interval '1' hour * (level - 1) as hr,
case when level = 1 and connect_by_isleaf = 1
then end_time - start_time
when level = 1
then trunc(start_time, 'hh') + interval '1' hour - start_time
when connect_by_isleaf = 1
then end_time - trunc(end_time, 'hh')
else interval '1' hour
end as duration
from your_table
connect by trunc(start_time, 'hh') + interval '1' hour * (level - 1) < end_time
and prior id = id
and prior sys_guid() is not null
;
Output:
ID HR DURATION
---------- ------------------- -------------------
1 2019-01-09 17:00:00 +00 00:55:26.000000
1 2019-01-09 18:00:00 +00 01:00:00.000000
1 2019-01-09 19:00:00 +00 00:55:03.000000
2 2019-01-09 23:00:00 +00 00:12:59.000000
2 2019-01-10 00:00:00 +00 01:00:00.000000
2 2019-01-10 01:00:00 +00 01:00:00.000000
2 2019-01-10 02:00:00 +00 00:05:03.000000
3 2019-01-09 18:00:00 +00 00:01:06.000000
4 2019-01-09 13:00:00 +00 01:00:00.000000
4 2019-01-09 14:00:00 +00 00:00:01.000000
I need to be able to calculate the duration (in seconds) between two time stamps as an aggregate over a time series using a default end_datetime if it is null.
Imagine you have something like a punch card when you puch in and out:
username, start_datetime, end_datetime
What I want is a generated time series of the last N minutes with the duration for all users that overlap within that time frame. So it would be the SUM(end_datetime - start_datetime) where you would COALESCE a default end_datetime if it is null.
So the basic pieces I think I need are:
Generate the time interval:
select TIMESTAMP '2013-01-01 12:01:00' - (interval '1' minute * generate_series(0,5)) as timestamps;
COALESCE a default end_datetime
COALESCE(end_datetime, NOW())
Figure out the seconds difference between the start and end dates
So if one user logged in at 11:56:50 and it is now 12:01:40 we should get a table like:
timestamps duration
-------------------------------------
2013-01-01 12:01:00 40
2013-01-01 12:00:00 60
2013-01-01 11:59:00 60
2013-01-01 11:58:00 60
2013-01-01 11:57:00 60
2013-01-01 11:56:00 10
with t as (select '2013-01-01 11:56:50'::timestamp startt, '2013-01-01 12:01:40'::timestamp endt)
select
timestamps,
extract(epoch from
case
when timestamps=date_trunc('minute',startt) then date_trunc('minute',startt) + interval '1 minute' - startt
when timestamps =date_trunc('minute',endt) then endt- date_trunc('minute',endt)
else interval '60 seconds' end) as durations
from
(select generate_series(date_trunc('minute',startt),date_trunc('minute',endt),'1 minute') timestamps, * from t) a
order by
timestamps desc;
2013-01-01 12:01:00;40
2013-01-01 12:00:00;60
2013-01-01 11:59:00;60
2013-01-01 11:58:00;60
2013-01-01 11:57:00;60
2013-01-01 11:56:00;10
If you have multiple rows with start and end timestamp than the following will work:
select
id,
timestamps,
extract(epoch from
case
when timestamps=date_trunc('minute',startt) then date_trunc('minute',startt) + interval '1 minute' - startt
when timestamps =date_trunc('minute',endt) then endt- date_trunc('minute',endt)
else interval '60 seconds' end) as durations
from
(
select
id,
generate_series(date_trunc('minute',startt) ,
coalesce(date_trunc('minute',endt),date_trunc('minute',Now())),'1 minute') as timestamps,
startt, endt
from test
) a
order by
id, timestamps desc
SQLFiddle