Transform days into hours in postgres - sql

I have Hours,Minute,Second, and days as results in the Postgres query. I want to convert everything into hours.
Example
Row 1 result: 19:53:45
Row 2 result: 1 day 05:41:58
Now I want to convert days into hours like below
Row 1 result:19:53:45
Row 2 result: 29:41:58
Can someone help me how to do it in the postgres sql?

cast(col as interval hour to minute) should work, according to Standard SQL.
Anyway, this seems to work:
col - extract(day from col) * interval '1' day -- remove the days
+ extract(day from col) * interval '24' hour -- and add back as hours
See fiddle

Presumably, you want the result as a string, because times are limited to 24 hours. You can construct it as:
select *,
(case when ar[1] like '%day%'
then (split_part(col, ' ', 1)::int * 24 + split_part(ar[1], ' ', 3)::int)::text ||
right(col, 6)
else col
end)
from (values ('19:53:45'), ('1 day 05:41:58')) v(col) cross join lateral
regexp_split_to_array(col, ':') a(ar);
You can also do this without a:
select *,
(case when col like '%day%'
then (split_part(col, ' ', 1)::int * 24 + (regexp_match(col, ' ([0-9]+):'))[1]::int)::text ||
right(col, 6)
else col
end)
from (values ('19:53:45'), ('1 day 05:41:58')) v(col) ;

Related

How to transpose in SQL?

This is my SQL query:
'{select id,value,
case when temp.country-id=2 then
interval 4 hour + cast(time as time ) else
interval 3 hour + cast(time as time ) end as TIME
from temp}'
This is the output I am getting for above query:
I want this output using SQL:

Generating multiple rows from one row in SQL based on a column

I have a table with these five columns:
The ID is the PI here. BEGIN_WINDOW and END_WINDOW are TIMESTAMP columns. The DURATION_DAYS_RUP is calculated by dividing DURATION_HRS by 24 and rounding up.
What I'm trying to do is based on the DURATION_DAYS_RUP, I need to create multiple rows.
If that value is 1, then the row created is just the same row with same ID, BEGIN_WINDOW and END_WINDOW.
If it's greater than 1, for ex. 2, two rows would be created - one row where the ID is the same, BEGIN_WINDOW is the value from the original row, and END_WINDOW is 24 hrs + BEGIN_WINDOW and the second row would be the same ID, BEGIN_WINDOW is the END_WINDOW of that first row, and END_WINOW is this row's BEGIN_WINDOW + 24 hours.
See the example below:
I've researched a lot but can't seem to find the trick to doing this. If anyone has an idea, would be greatly appreciated!
You could use Teradata's EXPAND ON syntax:
SELECT x.ID, BEGIN(pd) as BEGIN_WINDOW, BEGIN(pd) + INTERVAL '24' HOUR as END_WINDOW
FROM mytable x
EXPAND ON PERIOD(x.BEGIN_WINDOW, x.END_WINDOW) AS pd
BY INTERVAL '24' HOUR;
You can use a recursive query:
with recursive cte (id, begin_window, end_window, duration_days_rup) as (
select
id,
begin_window,
case when duration_days_rup = 1 then end_window else begin_window + interval '1' day end,
duration_days_rup - 1
from mytable
union all
select
id,
begin_window + interval '1' day,
case when duration_days_rup = 1 then end_window else end_window + interval '1' day end,
duration_days_rup - 1
from cte
where duration_days_rup > 0
)
select id, begin_window, end from cte
Looking at your query, I doubt that you really need the duray_days_rup column, which is derived information. We could use straight date comparisons. I think the logic you want is:
with recursive cte (id, begin_window, end_window, real_end_window) as (
select
id,
begin_window,
least(end_window, begin_window + interval '1' day),
end_window
from mytable
union all
select
id,
begin_window + interval '1' day,
least(real_end_window, end_window + interval '1' day),
real_end_window
from cte
where begin_window + interval '1' day > real_end_window
)
select id, begin_window, end from cte

SQL request with date

I have a table with data and columns containing a date but separated into several columns :
a column with the year,
a column with the month,
a column with the day.
I would like to keep only the data with a date that is less than 2 months.
I have tried to concat the date but the month and the day does not always have 2 characters. Sometimes it is one number : 1 for january for example.
Could you give some tips to make this request?
Thanks in advance,
select *
from etude
where concat(year,month,day) > NOW()
It is not working as expected
The TIMESTAMP_FORMAT() function should be robust to months/days being either one or two digits:
SELECT *
FROM etude
WHERE TIMESTAMP_FORMAT(CONCAT(year, month, day), 'YYYYMMDD') > NOW();
But note that while this may fix your immediate problem, a much better long term solution would be to stop storing the various components of your date in separate columns. Instead, just maintain a single bona-fide date/timestamp column.
Use lpad function along with timestamp_format function
select *
from etude
where timestamp_format(year || lpad(month,2,'0') || lpad(day,2,'0'), 'YYYYMMDD') > now();
If keep only the data with a date that is less than 2 months means keep only the data with a date that is not earlier than 2 months from the current date, then try this as is:
SELECT dt
FROM
(
SELECT date(digits(dec(year, 4)) || '-' || digits(dec(month, 2))|| '-' || digits(dec(day, 2))) dt
FROM table
(
values
(2019, 1, 1)
, (2019, 7, 13)
) etude (year, month, day)
)
WHERE dt > current date - 2 month;
to combine your 2 questions into 1 answer and using Satya's answer
select *
from etude
where timestamp_format(year || lpad(month,2,'0') || lpad(day,2,'0'), 'YYYYMMDD') > now() - 2 months;
This is another option
select *
from etude
where DATE('0001-12-31') + (year -2) YEARS + month MONTHS + day DAYS > CURRENT DATE

Convert TIMESTAMP difference column data to string format

From my view, I am getting a column with value like '0 0:0:0.343009' which shows the difference between two timestamps, to millisecond precision.
I would like to show them as a string like 343 milliseconds or other value but should be a millisecond conversion.
I had similar post in the past but that time column was a DATE datatype, and this time it is a TIMESTAMP. I am using the FLOOR function to change the output to a numeric value to show a more user friendly result.
I used sample query to find a difference of dates. Here created_time is a TIMESTAMP datatype:
select msg_guid,
(max(case when payload_type = 1 then created_time end) -
(case when max(case when payload_type = 2 then created_time end) <>
trunc(max(case when payload_type = 2 then created_time end))
then max(case when payload_type = 2 then created_time end)
when max(case when payload_type = 3 then created_time end) <>
trunc(max(case when payload_type = 3 then created_time end))
then max(case when payload_type = 3 then created_time end)
end)
) as diff
from table t
group by msg_guid;
When you add or subtract timestamps, the result is an interval, not another timestamp. You can use the extract function to pull the components out of that. If you value is always going to be sub-second you can just extract the seconds, and multiply by a thousand to get the milliseconds:
with t as (
select 1 as msg_guid,
interval '0 0:0:0.343009' day to second as diff
from dual
)
select trunc(extract (second from diff) * 1000)
from t;
TRUNC(EXTRACT(SECONDFROMDIFF)*1000)
-----------------------------------
343
Here your real query would take the place of the dummy CTE I used with an interval literal.
If the interval might be more than a second then you would presumably want to get the entire value in milliseconds, so you'd need to extract all the elements and add them together, multiplying each based on what they represent - so a full day would be 86400000 milliseconds etc.; the plain elements would come out like:
column diff format a25
with t as (
select 1 as msg_guid,
systimestamp - trunc(systimestamp) as diff
from dual
)
select diff,
extract (day from diff) as dd,
extract (hour from diff) as hh,
extract (minute from diff) as mi,
extract (second from diff) as ss
from t;
DIFF DD HH MI SS
---------------------- ---------- ---------- ---------- ----------
0 9:13:26.150627 0 9 13 26.150627
And you'd combine them like:
with t as (
select 1 as msg_guid,
systimestamp - trunc(systimestamp) as diff
from dual
)
select diff,
trunc(1000 * (
extract (day from diff) * (60*60*24)
+ extract (hour from diff) * (60*60)
+ extract (minute from diff) * 60
+ extract (second from diff)
)) as milliseconds
from t;
DIFF MILLISECONDS
---------------------- ------------
0 9:13:27.650365 33207650
But based on your previous question, maybe you want it as a string, as the separate components:
with t as (
select 1 as msg_guid,
systimestamp - trunc(systimestamp) as diff
from dual
)
select diff,
extract (day from diff) || ' DAYS '
|| extract (hour from diff) || ' HOURS '
|| extract (minute from diff) || ' MINUTES '
|| trunc(extract (second from diff)) || ' SECONDS '
|| (trunc(extract (second from diff) * 1000)
- (trunc(extract (second from diff)) * 1000)) || ' MILLISECONDS'
as text
from t;
DIFF TEXT
---------------------- -------------------------------------------------------
0 9:43:38.896007 0 DAYS 9 HOURS 43 MINUTES 38 SECONDS 896 MILLISECONDS
SQL Fiddle based on your sample data, sort of, and with the time calculation reversed so the value is positive.

SQL how to do 1 hour interval

I have sql query which returns record by date specified
What I want to do is group them by 1 hour interval
My query returns a date and interval.
interval value looks like this
8:00,8:30,9:00,9:30,10:00
as you can see the interval has produce 5 value what I want to do is group them by this
8:00-9:00,9:00-10:00
I have designed a query:
SELECT DATEPART(HOUR,VC.DATE+ VC.INTERVAL) AS DATE
,DATEPART(HOUR,VC.INTERVAL) AS INTERVAL
FROM VMUK_Q1R_IB_CONSOLIDATED VC
But the problem with this it display like this 8,8,9,9,10
How to I achieve this?
What you need is to create a set of hourly values and join back to it based on the hour part of your value. This will make sure the missing 'buckets' are represented. The following CTE will give you the lookup for 24 hours - you could do the same thing with a static lookup table too.
with ranges
as
(
select 0 as value
union all
select r.value+ 1 from ranges r where r.value <= 24
)
select
r.value start
from ranges r
You could fix this by a calculation or formatting. I think formatting would be simpler for this example. Try this:
SELECT Convert(VarChar(20), DATEPART(HOUR,VC.INTERVAL)) + ':00' AS DATE
,DATEPART(HOUR,VC.INTERVAL) AS INTERVAL
FROM VMUK_Q1R_IB_CONSOLIDATED VC
If you want the full date + time shown, rounded down, try this:
SELECT Convert(VarChar(20), VC.Date, 101) + Convert(VarChar(20), DATEPART(HOUR,VC.INTERVAL)) + ':00' AS DATE
,DATEPART(HOUR,VC.INTERVAL) AS INTERVAL
FROM VMUK_Q1R_IB_CONSOLIDATED VC
If you want time ranges too, try this:
SELECT Convert(VarChar(20), VC.Date, 101) + Convert(VarChar(20), DATEPART(HOUR,VC.INTERVAL)) + ':00' AS DATE,
,DatePart(HOUR,VC.INTERVAL)) + ':00 - ' + DatePart(HOUR, DateAdd(HOUR, VC.INTERVAL, 1)) + ':00' AS TimeRange
,DATEPART(HOUR,VC.INTERVAL) AS INTERVAL
FROM VMUK_Q1R_IB_CONSOLIDATED VC
SELECT
DATEPART(HOUR,VC.DATE+ VC.INTERVAL) AS DATE,
case DATEPART(HOUR,VC.INTERVAL)
when 0 then '00:00-00:59'
when 1 then '01:00-01:59'
.
.
etc.
.
.
when 22 then '22:00-22:59'
when 23 then '23:00-23:59'
end AS INTERVAL
FROM VMUK_Q1R_IB_CONSOLIDATED VC