Generating time series for everyday's am9:00 to pm2:00 - sql

I want to generatie time series from am9:00 to pm2:00 everyday
My current time series generated from min(ticktime) to max(ticktime)
But I want to scope the series to everyday's am8:59 to pm3:00
how to do it ?
I tried to generate time between 1:11 ~ 1:15, But it didn't stop by 1:15
SELECT generate_series
(
date_trunc('second', min(ticktime)) ,
date_trunc('second', max(ticktime)) ,
interval '1 sec'
) AS ticktime FROM czces
WHERE
(date_part('hour', ticktime) >= 1 AND date_part('minute', ticktime) > 10 )
AND (date_part('hour', ticktime) <= 1 AND date_part('minute', ticktime) <= 15 )
I tried another solution but the outputed generating series still not under the BETWEEN interval.
Please check the result the series not stopped by 1:01 am
SELECT generate_series
(
min(ticktime)::timestamp,
max(ticktime)::timestamp,
'1 minute'::interval
) AS ticktime FROM czces
where ticktime::time between '00:01 am'::time AND '1:01 am'::time
~~~~

You need to add the appropriate where clause.
Examples of the syntax to use:
select dt
from generate_series(
now()::date - interval '1 day',
now()::date + interval '1 day',
'1 hour'
) as dt
where dt::time between '9:00 am'::time and '2:00 pm'::time;
select t
from generate_series(
now()::timestamptz(0) - interval '1 day',
now()::timestamptz(0) + interval '1 day',
'1 hour'
) as t
where t::time between '9:00 am'::time and '2:00 pm'::time;

Related

Combine multiple selects for statistics generation into on result set

Hey Pros,
I am far away to have good knowledge about SQL, and would ask you to give me some hints.
Currently we aggregate our data with python and I would try to switch this when possible to. (SQL (Postgresql server)
My goal is to have one statment that generate an average for two seperates column's for specific time intervals (1 Hour, 1 Day, 1 Week, Overall) also all events in each period shoud be counted.
I can create 4 single statments for each interval but strugle how to combine this four selects into on result set.
select
count(id) as hour_count,
camera_name,
round(avg("pconf")) as hour_p_conf,
round(avg("dconf")) as hour_d_conf
from camera_events where timestamp between NOW() - interval '1 HOUR' and NOW() group by camera_name;
select
count(id) as day_count,
camera_name,
round(avg("pconf")) as day_p_conf,
round(avg("dconf")) as day_d_conf
from camera_events where timestamp between NOW() - interval '1 DAY' and NOW() group by camera_name;
select
count(id) as week_count,
camera_name,
round(avg("pconf")) as week_p_conf,
round(avg("dconf")) as week_d_conf
from camera_events where timestamp between NOW() - interval '1 WEEK' and NOW() group by camera_name;
select
count(id) as overall_count,
camera_name,
round(avg("pconf")) as overall_p_conf,
round(avg("dconf")) as overall_d_conf
from camera_events group by camera_name;
When possbile the result should look like the data on image
Some hints would be great, thank u
Consider conditional aggregation by moving WHERE logic to CASE statements in SELECT. Alternatively, in PostgreSQL use FILTER clauses.
select
camera_name,
count(id) filter(timestamp between NOW() - interval '1 HOUR' and NOW()) as hour_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 HOUR' and NOW())) as hour_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 HOUR' and NOW())) as hour_d_conf,
count(id) filter(timestamp between NOW() - interval '1 DAY' and NOW()) as day_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 DAY' and NOW())) as day_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 DAY' and NOW())) as day_d_conf,
count(id) filter(timestamp between NOW() - interval '1 WEEK' and NOW()) as week_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 WEEK' and NOW())) as week_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 WEEK' and NOW())) as week_d_conf,
count(id) as overall_count,
round(avg("pconf")) as overall_p_conf,
round(avg("dconf")) as overall_d_conf
from camera_events
group by camera_name;
The simplest way is to join them. For example:
select
coalesce(h.camera_name, d.camera_name, w.camera_name) as camera_name
h.hour_count, h.hour_p_conf, h.hour_d_conf
d.day_count, d.day_p_conf, d.day_d_conf
w.week_count, w.week_p_conf, w.week_d_conf
from (
-- hourly query here
) h
full join (
-- daily query here
) d on d.camera_name = h.camera_name
full join (
-- weekly query here
) w on w.camera_name = coalesce(h.camera_name, d.camera_name)

Skip operand in condition if column doesn't exists

I have next part of condition:
(COALESCE(some_day, now()) at time zone 'Some/TZ')::date - interval '1 day' + interval '1 day' * day_number
what I need is to skip - interval '1 day' if some_day is null. How I can do this in SQL?
You can put that piece of logic in the COALESCE():
COALESCE(some_day - interval '1 day' ,
now()) at time zone 'Some/TZ'
)::date + interval '1 day' * day_number

Set-returning functions are not allowed in CASE in postgreSQL

I am trying to run this query was able to till some time ago. I don't know what went wrong and I started getting this error now?
Your database returned: ERROR: set-returning functions are not allowed in CASE Hint: You might be able to move the set-returning function into a LATERAL FROM item.
My query:
SELECT distinct
(CASE
WHEN {PERIOD} = 'Previous Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date
THEN generate_series(pto.pto_start_date, pto.pto_end_date, '2 day'::interval)
WHEN {PERIOD} = 'Current Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date
THEN generate_series(pto.pto_start_date, pto.pto_end_date, '1 day'::interval)
ELSE
generate_series(pto.pto_start_date, pto.pto_end_date, '1 day'::interval)
END) AS dt
FROM cust_pto pto
Start dates and end Dates:
What has gone wrong?
Why you're getting the error now: you upgraded to postgres 10. Set returning functions are no longer allowed.
What to do: there is more than one way to accomplish what you're trying to do. For the sake of keeping it as close as possible to your original query, all you have to do is put your CASE statement inside generate_series:
SELECT distinct generate_series(
pto.pto_start_date,
pto.pto_end_date,
CASE
WHEN {PERIOD} = 'Previous Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date THEN
'2 day'::interval
ELSE
'1 day'::interval
END
) AS dt
FROM cust_pto pto

Postgres - Return 0 count for intervals with no data in date_trunc

I am trying to create a table that lists how many counts i have in 5 minute intervals over 10 days. I think my join is wrong since i am not getting the empty rows in my query.
select date_trunc('minute', activities.activitytime) -
(CAST(EXTRACT(MINUTE FROM activities.activitytime)
AS integer) % 5) * interval '1 minute' as day_column, count(activities.activityid)
from generate_series(current_date - interval '10 day', current_date, '1 minute') d
left join activities on date(activities.activitytime) = d
group by day_column
order by day_column;
You are close. But the key idea is that you need to use the columns from the generate_series() for the group by key:
select d.dte, count(a.activitytime)
from generate_series(current_date - interval '10 day', current_date, '5 minute') d(dte) left join
activities a
on a.activitytime >= d.dte and a.activitytime < d.dte + interval '5 minute'
group by d.dte
order by d.dte;

Converting Recursive Query from Week over Week to Month over Month

I have a recursive query that provides the number of orders placed week over week (week_no, week_start, and week_end). I'd like to create a similar breakdown for a month over month analysis.
WITH recursive weeks (week_start, week_end, time_end, weekno) AS (
VALUES ('2015-12-27'::date, '2016-01-02'::date, '2016-04-02'::date, 1)
UNION ALL
SELECT (week_end + interval '1 day')::date,
(CASE
WHEN (week_end + interval '7 days')::date > time_end THEN time_end
ELSE (week_end + interval '7 days')::date
END)::date,
time_end,
weekno+1
FROM weeks
WHERE time_end > week_end)
Any help would be greatly appreciated.
Why would you use a recursive query for this? Use generate_series():
select g.week_start, g.week_start + interval '6 day' as week_end,
row_number() over (order by g.week_start) as weeknum
from generate_series('2015-12-27'::timestamp,
'2016-01-02'::timestamp,
interval '1 week'
) g(week_start);
The equivalent for months would be like:
select g.month_start, g.month_start + interval '1 month' - interval '1 day' as month_end,
row_number() over (order by g.month_start) as monthnum
from generate_series('2015-12-01'::timestamp,
'2016-01-01'::timestamp,
interval '1 month'
) g(month_start);