I have created a query which sums up the interval for respective date-time,
select sum(ts_polling) / count(ts_polling) as Average_Queue_Wait_Time , cast(time_start AS Date)
from callcent_queuecalls group by cast(time_start AS date) order by time_start DESC;
Is there a way to convert Average_Queue_Wait_Time from interval data type to number ?
You can get the number of seconds in an interval like this:
SELECT EXTRACT(epoch FROM INTERVAL '1 day 30 minutes 1.234 seconds');
┌───────────┐
│ date_part │
├───────────┤
│ 88201.234 │
└───────────┘
(1 row)
Related
I want this time minus 5 minutes
I tried current_date but it gives me an other time than cet.
I need to get current date minus 5 minutes in postrgesql
SELECT now() AT TIME ZONE 'Europe/Brussels' AS europe ;
SELECT current_timestamp AT TIME ZONE 'Europe/Brussels' - INTERVAL '5 minutes';
Produces:
┌───────────────────────────┐
│ column │
├───────────────────────────┤
│ 2021-03-24 12:16:55.94187 │
└───────────────────────────┘
https://www.postgresql.org/docs/current/functions-datetime.html
I would like to generate an array of all the time ranges that fall within 4 pm-9 pm between a start and end date
start_date | end_date
----------------------------------------
2020-11-01 16:30:00 | 2020-11-03 18:30:00
The query should be able to turn the above table into:
row | start_date | end_date
------------------------------------------------
1 | 2020-11-01 16:30:00 | 2020-11-01 21:00:00
------------------------------------------------
2 | 2020-11-02 16:00:00 | 2020-11-02 21:00:00
------------------------------------------------
3 | 2020-11-03 16:00:00 | 2020-11-03 18:30:00
Could someone point me in the right direction on how to approach this?
I would do it like this:
with input as (
select '2020-11-01 16:30:00'::timestamptz as start_date,
'2020-11-03 18:30:00'::timestamptz as end_date
)
select row_number() over (order by ddate) as row,
case
when start_date::date = ddate
and start_date > ddate + interval '16 hours'
then start_date
else ddate + interval '16 hours'
end as start_date,
case
when end_date::date = ddate
and end_date < ddate + interval '21 hours'
then end_date
else ddate + interval '21 hours'
end as end_date
from input
cross join lateral
generate_series(
case
when start_date::time > '21:00' then start_date::date + interval '1 day'
else start_date::date
end,
case
when end_date::time < '16:00' then end_date::date - interval '1 day'
else end_date::date
end,
interval '1 day') as gs(ddate)
;
┌─────┬────────────────────────┬────────────────────────┐
│ row │ start_date │ end_date │
├─────┼────────────────────────┼────────────────────────┤
│ 1 │ 2020-11-01 16:30:00-05 │ 2020-11-01 21:00:00-05 │
│ 2 │ 2020-11-02 16:00:00-05 │ 2020-11-02 21:00:00-05 │
│ 3 │ 2020-11-03 16:00:00-05 │ 2020-11-03 18:30:00-05 │
└─────┴────────────────────────┴────────────────────────┘
(3 rows)
When you use generate_series with hours instead of days as in the solution of #MikeOrganek, you can filter them directly. Start and end hours should be "complete hours", which can be achieved using date_trunc(). Because date_trunc cuts the final minutes of the end date, it makes sense, to create the series an hour more; this is why an hour is added in the example.
Finally GROUP BY date and give out the MIN and MAX time. With least() and greatest() you can adjust the edge cases.
step-by-step demo:db<>fiddle
SELECT
GREATEST(MIN(gs), start_date) as start_time,
LEAST(MAX(gs), end_date) as end_time
FROM
t,
generate_series(
date_trunc('hour', start_date),
date_trunc('hour', end_date) + interval '1 hour',
interval '1 hour'
) as gs
WHERE gs::time >= '16:00:00' and gs::time <= '21:00:00'
GROUP BY start_date, end_date, gs::date
ORDER BY gs::date
Another solution is to use generate_series() with a one day interval and construct the time part depending on the value of the time in start/end date for the first and last day.
with from_to (start_date, end_date) as (
values (timestamp '2020-11-01 16:30:00', timestamp '2020-11-03 18:30:00')
)
select g.nr as row,
g.d::date + case
when g.d::date = start_date::date and start_date::time > time '16:00' then start_date::time
else time '16:00'
end as start_date,
g.d::date + case
when g.d::date = end_date::date and end_date::time < time '21:00' then end_date::time
else time '21:00'
end as end_date
from from_to
cross join generate_series(start_date, end_date, interval '1 day') with ordinality as g(d,nr)
order by g.nr;
Online example
Hello I am a beginner at SQL, especially postgresql.
I have a table that looks something like this:
ID | Entity | Startdate | enddate
------| ------ | ------ | ------
1 | Hospital |2013-01-01 |2013-01-31
1 | Clinic |2013-02-01 |2013-04-30
1 | Hospital |2013-05-01 |2013-05-31
What I would like to do in this case is that where the start and end date span more than a month to break it out so the above table would like this:
ID | Entity | Startdate | enddate
------| ------ | ------ | ------
1 | Hospital |2013-01-01 |2013-01-31
1 | Clinic |2013-02-01 |2013-02-29
1 | Clinic |2013-03-01 |2013-03-31
1 | Clinic |2013-04-01 |2013-04-30
1 | Hospital |2013-05-01 |2013-05-31
If you notice that row 2, 3 and 4 have been broken down by the month and the ID and entity have also been duplicated.
Any suggestions on how to run this in postgresql would be appreciated.
P.S Apologies I am trying to figure out how to create the table above properly. Having difficulty, the pipes between the numbers and words are lines in a table.
Hope its not too confusing.
One way to do this is to create yourself an end_of_month function like this:
CREATE FUNCTION end_of_month(date)
RETURNS date AS
$BODY$
select (date_trunc('month', $1) + interval '1 month' - interval '1 day')::date;
$BODY$
LANGUAGE sql IMMUTABLE STRICT
COST 100;
Then you can have a string of UNIONS like this:
SELECT
id,
entity,
startdate,
least(end_of_month(startdate),enddate) enddate
from hospital
union
SELECT
id,
entity,
startdate,
least(end_of_month((startdate + interval '1 month')::date),enddate) enddate
from hospital
union
id,
entity,
startdate,
least(end_of_month((startdate + interval '1 month')::date),enddate) enddate
from hospital
ORDER BY startdate,enddate
The problem with this approach, is that you need to have as many unions as necessary!
The alternative is to use a cursor.
EDIT
Just thought of another (better) non-cursor solution. Create a table of month-end dates. Then you can simply do:
select h.id,
h.entity,
h.startdate,
least(h.enddate, m.enddate) enddate
from hospital h
INNER JOIN monthends m
ON m.enddate > h.startdate and m.enddate <= end_of_month(h.enddate)
ORDER BY startdate, enddate
Here is example how to clone row based on its data:
-- Demo data begin
with t(i,x,y) as (values
(1, '2013-02-03'::date, '2013-04-27'::date),
(2, current_date, current_date))
-- Demo data end
select
*,
greatest(x, z)::date as x1, least(y, z + '1 month - 1 day'::interval)::date as y1
from
t,
generate_series(date_trunc('month', x)::date, date_trunc('month', y)::date, '1 month') as z;
┌───┬────────────┬────────────┬────────────────────────┬────────────┬────────────┐
│ i │ x │ y │ z │ x1 │ y1 │
╞═══╪════════════╪════════════╪════════════════════════╪════════════╪════════════╡
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-02-01 00:00:00+02 │ 2013-02-03 │ 2013-02-28 │
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-03-01 00:00:00+02 │ 2013-03-01 │ 2013-03-31 │
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-04-01 00:00:00+03 │ 2013-04-01 │ 2013-04-27 │
│ 2 │ 2017-08-27 │ 2017-08-27 │ 2017-08-01 00:00:00+03 │ 2017-08-27 │ 2017-08-27 │
└───┴────────────┴────────────┴────────────────────────┴────────────┴────────────┘
Just remove Demo data block and replace t, x and y by your table/columns names.
Explanation:
least() and greatest() function returns minimum and maximum element accordingly. Link
generate_series(v1,v2,d) function returns series of values started with v1, not greatest then v2 with step d. Link
'1 month - 1 day'::interval - interval data type notation, <value>::<datatype> means explicit type casting, the SQL standard equivalent is cast(<value> as <datatype>). Link and link
date_trunc() function truncates the date/timestamp value to the specified precision. Link
I have a table with the following fields:
my_date my_time my_time2
2017-04-14 10:00:01 286115
How do I combine these fields into timestamp column like 2017-04-14 10:00:01.286115?
Unfortunately select my_date+my_time+my_time2 from my_table works fine only for the first two fields. Thanks in advance.
select date '2017-04-14' + time '10:00:01' + 286115 * interval '1usec';
┌────────────────────────────┐
│ ?column? │
╞════════════════════════════╡
│ 2017-04-14 10:00:01.286115 │
└────────────────────────────┘
(1 row)
I have a query where I count the number of rows for each year:
SELECT
count(*) as activation_count
, extract(year from activated_at) as year
FROM "activations"
WHERE ...
GROUP BY year
But I'd like instead to have years ranging from September to September instead of January to January. Thus grouping by school years instead of calendar years.
Can I modify my query to do that?
And more generally, is it possible to group by a time range and specify an offset to it, eg: extract(year from activated_at offset interval 2 month) as year (this is not working, just the idea of what I want)
What you essentially want is to treat all dates after September as "next year", so the following should work:
select count(*) as activation_count,
case
when extract(month from activated_at) >= 9 then extract(year from activated_at) + 1
else extract(year from activated_at)
end as school_year
from activations
group by school_year;
Assuming that someone whose activated_at is '2016-09-01' should be counted as year = 2017, you could add 4 months to activated_at when in the extract (translating (in the mathematical sense of the word) September to January).
SELECT * FROM activations ;
┌────────────────────────┐
│ activated_at │
├────────────────────────┤
│ 2016-08-01 00:00:00+02 │
│ 2016-09-01 00:00:00+02 │
│ 2016-10-01 00:00:00+02 │
│ 2017-02-02 00:00:00+01 │
└────────────────────────┘
(4 rows)
SELECT COUNT(*),
EXTRACT(year FROM (activated_at + '4 months'::interval)) AS year
FROM activations
GROUP BY year;
┌───────┬──────┐
│ count │ year │
├───────┼──────┤
│ 3 │ 2017 │
│ 1 │ 2016 │
└───────┴──────┘
(2 rows)
If it should be counted as year = 2016 you could remove 8 months instead.