format interval with to_char - sql

Following SQL command
select TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00'))) from table1
produces a result of the format: +000000000 00:03:01.954000.
Is it possible to enter a special format in the to_char function in order to get
a result of format: +00 00:00:00.000?

you could cast the result if you want less precision:
SQL> SELECT TO_DSINTERVAL('10 10:00:00') t_interval FROM dual;
T_INTERVAL
-----------------------------------------------------------
+000000010 10:00:00.000000000
SQL> SELECT CAST(TO_DSINTERVAL('10 10:00:00')
2 AS INTERVAL DAY(2) TO SECOND(3)) t_interval
3 FROM dual;
T_INTERVAL
-----------------------------------------------------------
+10 10:00:00.000
Edit following OP comment:
From The Oracle Documentation (11gr1):
Interval datatypes do not have format models. Therefore, to adjust their presentation, you must combine character functions such as EXTRACT and concatenate the components.
It seems you will have to manually use EXTRACT to achieve the desired output:
SQL> SELECT to_char(extract(DAY FROM t_interval), 'fmS99999') || ' ' ||
2 to_char(extract(HOUR FROM t_interval), 'fm00') || ':' ||
3 to_char(extract(MINUTE FROM t_interval), 'fm00') || ':' ||
4 to_char(extract(SECOND FROM t_interval), 'fm00.000')
5 FROM (SELECT TO_DSINTERVAL('10 01:02:55.895') t_interval FROM dual)
6 ;
TO_CHAR(EXTRACT(DAYFROMT_INTER
------------------------------
+10 01:02:55.895
This is not very elegant but it seems it is the only way to deal with microseconds precision.

to_char() seems to have fixed format :( so regexp_substr may be an option, e.g.:
SELECT regexp_substr (TO_DSINTERVAL ('10 10:00:00'), '\d{2} \d{2}:\d{2}:\d{2}\.\d{3}') t_interval FROM dual

I realize it's not clever at all, nor is it the special format string you're looking for, but this answer does work, given that the output is fixed length:
SELECT SUBSTR(TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00'))), 1, 1)
|| SUBSTR(TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00'))), 9, 2)
|| ' '
|| SUBSTR(TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00'))), 12, 12)
FROM table1;
It also just truncs the fractional seconds instead of rounding, but I assume from your example they're all just zeros anyway.
This is an even greater embarrassment, but I couldn't resist:
SELECT SUBSTR(REPLACE(TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00')))
, '0000000', '')
, 1, 16)
FROM table1;

Slight case of thread necromancy, however I came across this question while searching for how to format an interval, so I thought it was worth adding this comment.
From the Oracle documentation, adding a timestamp to an interval results in a timestamp, so by adding a constant timestamp with zero time elements you can then use the standard to_char format elements for datetime ...
SELECT TO_CHAR( TIMESTAMP'1969-12-31 00:00:00' + TO_DSINTERVAL('0 00:03:01.954321'),
'HH24:MI:SS.FF3' ) FROM dual;
However, there is an issue if you intervals could be greater than a day. There is no format element for days that will yield 0. "DDD" is day of the year, so would be 365 in the example above, or 1 or more if the interval was greater then a day. This is fine as long as your intervals are less than 24 hours though.
Should add this is on 11g so may well not have be applicable to the OP.

SELECT W.SHIFT_NUMB || ' c: ' ||
TO_CHAR(TO_DATE('01.01.2012', 'dd.mm.yyyy') + W.TIMEFROM, 'HH24:MI') ||
' по: ' ||
TO_CHAR(TO_DATE('01.01.2012', 'dd.mm.yyyy') + W.TIMETO, 'HH24:MI'),
w.ID
FROM AC_WORK_SHIFT W
WHERE W.CLIENT_ID = GC
Just add date and use to_char ('HH24:MI') !

You can strip out the last part (or any part) with regular expression
Oracle REGEXP_REPLACE does just that.
select REGEXP_REPLACE( TO_CHAR(NVL(arg1 - arg2, TO_DSINTERVAL('0 00:00:00'))), '..*') from table1

SQL> SELECT
2 TO_CHAR(TIMESTAMP '1969-12-31 00:00:00' + to_dsinterval('0 00:03:01.954321'),
3 '"T minus" HH24 "hours" MI "minutes" SS.FF3 "seconds."'
4 ) AS elapsed_time
5 FROM
6 dual;
ELAPSED_TIME
-------------------------------------------
T minus 00 hours 03 minutes 01.954 seconds.

Related

Oracle SQL To_Char(interval) is not using my format string?

In the following bit of SQL, I want to print an interval (actually a number of seconds) in HH:MM:SS format. I convert the seconds to an Interval, then attempt to use TO_CHAR date formatting. The Oracle server ignores my pattern and prints a whole timestamp with zero chars in dates and microseconds. What am I doing wrong?
CASE WHEN (TR.RUN_LENGTH > 0) THEN TO_CHAR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second'), 'HH24:MI:SS')
ELSE '0' END AS RUN_LENGTH,
And I get: +000000000 00:03:22.000000000
As you have observed, to_char() doesn't work on intervals. So, don't think in terms of intervals. Instead, you have a number of seconds and you want to convert to a string in the format of HH:MM:SS.
Use arithmetic and string operations:
select lpad(floor(tr.run_length / 3600), 2, '0') || ':' || lpad(mod(floor(tr.run_length / 60), 60), 2, '0') || ':' || lpad(mod(tr.run_length, 60), 2, '0') as hhmmss
from (select 1 as run_length from dual union all
select 10 as run_length from dual union all
select 100 as run_length from dual union all
select 1000 as run_length from dual union all
select 10000 as run_length from dual union all
select 100000 as run_length from dual
) tr;
Here is a db<>fiddle.
I searched through the other Related responses; It's clear that Oracle doesn't actually implement a user specified format for TO_CHAR(DSINTERVAL). As some have suggested, you can add a date then use the date formatting. But that fails if the interval is more than 24 hours.
I ended up using REGEXP_SUBSTRING() but then ran into the problem that Oracle evidently doesn't support non-capturing groups. I was able to piece together this:
REGEXP_SUBSTR(TO_CHAR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second')), '^([\+0:\ ]*)(.*)(\.)',1,1,NULL,2 )
which seems to work quite well. It suppresses any leading zeros and fractional seconds.
If your interval is always less than 24 hours, then you can use one of these:
TO_CHAR(TRUNC(SYSDATE) + NUMTODSINTERVAL(TR.RUN_LENGTH, 'second'), 'hh24:mi:ss')
REGEXP_SUBSTR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second'), '\d{2}:\d{2}:\d{2}')
Otherwise you need to use the EXTRACT function:
SELECT
TO_CHAR(
EXTRACT(DAY FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second')) * 24
+ EXTRACT(HOUR FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second')) )
|| ':' EXTRACT(MINUTE FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second')
|| ':' TRUNC(EXTRACT(SECOND FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second'))
Optionally you have to add LPAD(..., 2, '0') around the parts, if required for your output.
Less code:
to_number(
substr(to_char((LATER_timestamp - EARLIER_timestamp),'HH24:MI:SS'), 1,10) /* this is the DAY string */
) || 'd ' ||
substr(to_char((LATER_timestamp - EARLIER_timestamp),'HH24:MI:SS'),12,11) /* this is the TIME string. Milliseconds are "FLOORED". */
Example output is: "2d 02:59:57.31".
Try this:
TO_CHAR (TRUNC (SYSDATE) + NUMTODSINTERVAL ((TR.RUN_LENGTH, 'second'),
'hh24:mi:ss' )
The doc for NUMTODSINTERVAL says it returns a string literal, so to_char has no effect. This hack above just takes the current date/time (sysdate), truncates the HH:mm:ss component, then adds back the #seconds you specify, then format that to just show the HH:MM:SS value

How can i pass variable with in single quote oracle sql

select
to_char(sysdate + interval '2' hour,'hh12:mi AM') as Time
from dual
i have a query that will add 2 hours from current system time , but it may add aur subtract the time and also hour's value will also be dynamic
so i have to use operator value it may + or - and similarly hour value it may 2 ,3 ,4 or 5 so my query will be
select
to_char(sysdate :operator interval ':hourvalue' hour,'hh12:mi AM') as Time
from dual
it gives me error
ORA-00907: missing right parenthesis
please help me out i am using oracle 11g
You cannot have an INTERVAL literal of a variable amount; however, you can have a fixed INTERVAL literal and then multiply it by a bind variable:
SELECT TO_CHAR(
SYSDATE + :hourvalue * INTERVAL '1' HOUR,
'hh12:mi AM'
) AS TIME
FROM DUAL
If you want a negative amount then just specify a negative :hourvalue rather than using a separate :operator bind variable.
You can use NUMTODSINTERVAL(n, 'hour') where n can has a negative value as well.
For example
SELECT NUMTODSINTERVAL((-1)*10, 'hour') h FROM DUAL;
You can also create an interval -(1 hour 37 min 41 sec):
SELECT NUMTODSINTERVAL((-1) * (1*3600 + 37*60 + 41), 'second') hms FROM DUAL;
Note, n is a number (decimal), so this will work too:
SELECT NUMTODSINTERVAL((-1) * (1 + 37/60 + 41/3600), 'hour') hms FROM DUAL;
It's only possible to use bind variables for arguments, you cannot use operators as bind variables. A workaround is to multiply the interval by -1 for negative and 1 for positive.
SELECT
TO_CHAR(
SYSDATE + numtodsinterval((CASE WHEN :operator = '-' THEN -1 ELSE 1 END * :interval),'hour'),
'hh12:mi AM'
) as time
FROM DUAL;

How to convert number format of a calculated column to time (hh:mm:ss) format, sql query

First I'm sorry for the poor English but I'll try to be as understandable as possible.
I'm trying to make a Oracle Database SQL Query which subtracts the datetime value of two existing columns, I managed how to do it using the instruction shown below but the column TEMPO that I created is returning with NUMBER format.
SELECT M.NUMOS, M.CODFUNCOS, M.DTINICIOOS, M.DTFIMSEPARACAO,
M.DTFIMSEPARACAO - M.DTINICIOOS AS TEMPO
FROM PCMOVENDPEND M
WHERE DTFIMSEPARACAO IS NOT NULL
AND DATA >= SYSDATE-1
The Output is at it goes.
Output
I need to convert the column TEMPO format to TIME as 'hh:mm:ss', and if some results passes through 24 hours it keeps adding like 32:01:20 (just like the [hh]:mm:ss Excel formatting).
I tried a few things like the instruction below but it returns ORA-00932 error inconsistent data type, expected TIME and returned DATE JULIAN (I don't know if it is the exact error description, my DBX is in Portuguese), unfortunately I can't do any changes to the root tables if it would help.
SELECT M.NUMOS, M.CODFUNCOS, M.DTINICIOOS, M.DTFIMSEPARACAO,
CAST(M.DTFIMSEPARACAO - M.DTINICIOOS AS TIME) AS TEMPO
FROM PCMOVENDPEND M
WHERE DTFIMSEPARACAO IS NOT NULL
AND DATA >= SYSDATE-1
Anyways, it is so important that the column TEMPO format is TIME because I'll use the sql query as an instruction to PowerBi Direct Query and I can't import the database to work it with PowerQuery since it is too large data.
Thanks everyone!
A function makes it prettier. It returns number of days formatted as dd:hh:mi:ss.
SQL> create or replace function f_days2ddhhmiss (par_broj_dana in number)
2 return varchar2
3 is
4 l_broj_dana number := par_broj_dana;
5 retval varchar2 (20);
6 begin
7 with podaci
8 as (select trunc (l_broj_dana) broj_dana,
9 round (mod (l_broj_dana * 24, 24), 2) broj_sati,
10 round (mod (l_broj_dana * 24 * 60, 60), 2) broj_minuta,
11 round (mod (l_broj_dana * 24 * 60 * 60, 60), 2)
12 broj_sekundi
13 from dual)
14 select lpad (p.broj_dana, 2, '0')
15 || ':'
16 || lpad (trunc (p.broj_sati), 2, '0')
17 || ':'
18 || lpad (trunc (p.broj_minuta), 2, '0')
19 || ':'
20 || lpad (trunc (p.broj_sekundi), 2, '0')
21 into retval
22 from podaci p;
23
24 return retval;
25 end f_days2ddhhmiss;
26 /
Function created.
Example:
Without it, you get decimal number:
SQL> select to_date('07.08.2020 14:25', 'dd.mm.yyyy hh24:mi:ss')
2 - to_date('03.08.2020 13:20', 'dd.mm.yyyy hh24:mi:ss') result
3 from dual;
RESULT
----------
4,04513889
With it, you get what you wanted:
SQL> select f_days2ddhhmiss(to_date('07.08.2020 14:25', 'dd.mm.yyyy hh24:mi:ss')
2 - to_date('03.08.2020 13:20', 'dd.mm.yyyy hh24:mi:ss')
3 ) result
4 from dual;
RESULT
--------------------------------------------------------------------------------
04:01:05:00
SQL>
Yes, such (or similar) code can be used directly in SQL, but it makes a SELECT statement kind of ugly and difficult to read.
Your query would then be
SELECT m.numos,
m.codfuncos,
m.dtinicioos,
m.dtfimseparacao,
f_days2ddhhmiss (m.dtfimseparacao - m.dtinicioos) AS tempo
FROM pcmovendpend m
WHERE dtfimseparacao IS NOT NULL
AND data >= SYSDATE - 1
See if it helps.
With Littlefoots help I managed a way to convert the column to the desired format, just as follows:
CREATE OR REPLACE FUNCTION WMS_CORINGA(TEMPO NUMBER)
RETURN VARCHAR2 IS
HORA NUMBER;
MINUTO NUMBER(2);
SEGUNDO NUMBER(2);
TEMPOABS NUMBER;
BEGIN
IF TEMPO <> 0 THEN
TEMPOABS := ABS(TEMPO);
HORA := TRUNC(TEMPOABS*24);
MINUTO := TRUNC(((TEMPOABS*24)-HORA) * 60);
SEGUNDO := ((((TEMPOABS*24)-HORA) * 60) - MINUTO) * 60;
RETURN TO_CHAR(HORA, 'FM9999900') || ':'|| TO_CHAR(MINUTO, 'FM00') || ':' || TO_CHAR (SEGUNDO, 'FM00');
ELSE
RETURN '';
END IF;
END;
SELECT M.NUMOS, M.CODFUNCOS, M.DTINICIOOS, M.DTFIMSEPARACAO,
WMS_CORINGA(M.DTFIMSEPARACAO - M.DTINICIOOS) AS TEMPO_EM_SEPARACAO
FROM PCMOVENDPEND M
WHERE DTFIMSEPARACAO IS NOT NULL
AND DATA>= SYSDATE-1
But I couldn't make the PowerBI connection, the output is Details: "This native database query isn't currently supported."
Anyways, learned a lot.
You can cast dates to timestamps before subtracting. The result of the subtraction is an interval represented as "days hh24:mi:ss.fff". If necessary you can then use the extract function to pull out the individual components. That way you have neither an ugly query nor a a need to create a function.
with the_dates( dt1, dt2) as
( select to_date('07.08.2020 14:25', 'dd.mm.yyyy hh24:mi:ss')
, to_date('03.08.2020 13:20', 'dd.mm.yyyy hh24:mi:ss')
from dual
)
select dt1, dt2, cast(dt1 as timestamp) - cast(dt2 as timestamp) result
from the_dates;

to_date function gives out oracle ORA 01850 : Hour should be between 0 and 23 error

I have hour and minute column in my table stored as datatype number. I'm trying to deduct 90 mins by converting them to valid date format and using to_char converting them to valid time format. I get the mentioned error.
I realized that this error is coming for data where i have hours entered as single number. for example 9 instead of 09. I tried LPAD but did not work as int or number doesn't take a 0 when padding.
to_char(to_date ( "hour_column" || "minute_column", 'hh24mi' ) - 90 / (24 * 60), 'hh24:mi') AS "Max_TIME"
Ora 08150: hour should be between 0 and 23.
You can apply a FORMAT adding leading zeroes, e.g.
to_char(to_date ( to_char("hour_column" * 100 + "minute_column", '0000'), 'hh24mi' ) - 90 / (24 * 60), 'hh24:mi') AS "Max_TIME"
The correct way to convert a one- or two-digit number to a two-digit string (with leading zeros, if necessary) is with the TO_CHAR() function, with the proper format model. The format model '00' is what you need; but that model will generate a three character string, leaving a space for the algebraic sign (plus is omitted by default, space is used as placeholder; if the number were negative, you would see the minus sign). Add the fm format model modifier to get just the two-digit number without a leading space.
Try to read the solution below step by step; with some luck, you will understand it all in a single reading. The WITH clause is there to generate some test inputs (it's not part of the solution!)
Final note - get in the habit of NOT using case-sensitive column names, which require double-quotes. Name your columns whatever you like, without double-quotes; then the names are not case sensitive, and you can write them in lower case, upper case, whatever, in your queries that need to reference them. If you name them with double-quotes, then you must always reference them in double quotes AND remember the exact capitalization you used when you created the table. Good luck remembering that "Max_TIME" was written in that capitalization!
with
test_data("hour_column", "minute_column") as (
select 3, 45 from dual union all
select 23, 50 from dual union all
select 1, 15 from dual union all
select 1, 30 from dual union all
select 0, 0 from dual
)
select "hour_column", "minute_column",
to_char( to_date( to_char("hour_column" , 'fm00') ||
to_char("minute_column", 'fm00') , 'hh24mi')
- interval '90' minute
, 'hh24:mi') as "Max_TIME"
from test_data
;
hour_column minute_column Max_TIME
----------- ------------- --------
3 45 02:15
23 50 22:20
1 15 23:45
1 30 00:00
0 0 22:30
If you like hacks, here's a hack - do an arithmetic computation with minutes (add one full day and then take modulo 24 * 60, to get the correct result when the input time is before 01:30) and then apply substr() to an interval data type. WITH clause and output not shown (they are the same as above).
select "hour_column", "minute_column",
substr( numtodsinterval(
mod((24 + "hour_column") * 60 + "minute_column" - 90, 24 * 60)
, 'minute') , 12, 5) as "Max_TIME"
from test_data
;
I would recommend to use the INTERVAL DAY TO SECOND Data Type rather than separate columns for hour and minute. If you cannot change the data type in your table then the solution could be
"hour_column" * INTERVAL '1' HOUR + "minute_column" * INTERVAL '1' MINUTE
or
NUMTODSINTERVAL("hour_column", 'hour') + NUMTODSINTERVAL("minute_column", 'minute')
Then you can run your arithmetic, for example
("hour_column" * INTERVAL '1' HOUR + "minute_column" * INTERVAL '1' MINUTE) - INTERVAL '90' MINUTE AS "Max_TIME"
This solution works also for Hours > 23 or Minutes > 59
Is this what you want?
SELECT
to_date(to_char(case when
hour_column<10
then '0'||hour_column else
hour_column end ||
"minute_column", 'hh24mi' ) - 90 /
(24 * 60), 'hh24:mi') AS "Max_TIME"
from table

Oracle SQL: Converting a flexible integer into time

i can convert a 4-digit-number (2300 e.g.) into (23:00) with the following Statement:
select to_char ( to_date ( 2300, 'HH24MI'), 'HH:MI') integer_time from dual;
Result:
INTEGER_TIME
--------
11:00
But how can I help myself if the values in the database are in addition stored as three digit value, if the first value is below then 10 (e.g. '937' for 9:37).
I kinda Need a double check Statement for this, anyone got a clue?
You need to LPAD it with '0' first
SELECT LPAD('937', 4, '0') FROM dual
A call to LPAD(.., 4) with a 4 character string is a no-op
SELECT LPAD('1937', 4, '0') FROM dual
You can use TO_CHAR with the format 'fm0999' if the value is stored as a NUMBER
select to_date(to_char(937, 'fm0999'), 'HH24MI') from dual;
see format models
As an alternative, you could avoid bouncing through a nominal date and just use number and string manipulation:
select to_char(trunc(937/100), 'FM00') ||':'|| to_char(mod(937, 100), 'FM00') from dual;
TO_CHAR
-------
09:37
... though if your starting value is actually a string rather than a number there's another level of implicit conversion going on. (If it is a string you probably have bigger problems with validation, but the lpad route might be the simplest way if you assume the data is always as expected).
With a range of values:
with t (n) as (
select 0 from dual
union all select 1 from dual
union all select 59 from dual
union all select 937 from dual
union all select 2300 from dual
union all select 2359 from dual
)
select n, to_char(trunc(n/100), 'FM00') ||':'|| to_char(mod(n, 100), 'FM00')
as integer_time
from t;
N INTEGER
---------- -------
0 00:00
1 00:01
59 00:59
937 09:37
2300 23:00
2359 23:59
If you don't want the leading zero - i.e. 937 instead of 09:37 - then change the first format model to FM90.
If your data isn't constrained to be integers in the range 0-2359 then this would format 'bad' times, possibly with hashes for the hour part, while bouncing through a date would error. Neither is ideal, but hopefully is a moot point.