Postgresql combining date, time and microseconds into timestamp - postgresql

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)

Related

postgresql how can i minus 5 minutes in date

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

How to add dates with dataset that have a start and end date?

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

Convert interval to number in postgresql

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)

Group by school year in postgres

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.

query for median with case statement

How to get the median of the valcolumn from table test whose value is greater than 20.
id val
1 5.43
2 106.26
3 14.00
4 39.58
5 27.00
In this case output would be median(27.00, 39.58, 106.26) = 39.58.
I am using PostgreSQL database.
Any help would be much appreciated.
From PostgreSQL 9.4 you use ordered aggregates:
postgres=# SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY val)
FROM foo WHERE val > 20;
┌─────────────────┐
│ percentile_cont │
╞═════════════════╡
│ 39.58 │
└─────────────────┘
(1 row)
or some really modern syntax:
postgres=# SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY val)
FILTER (WHERE val > 20)
FROM foo;
┌─────────────────┐
│ percentile_cont │
╞═════════════════╡
│ 39.58 │
└─────────────────┘
(1 row)