How can i pass variable with in single quote oracle sql - 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;

Related

Oracle APEX report - processing a field value

I have been creating Oracle APEX reports for a while.
I need to processes a column value to charge it's format.
The current SQL looks like this. All is good.
select PP_RUNDATA.ID as ID,
PP_RUNDATA.PP_START_TIME as PP_START_TIME,
PP_RUNDATA.PP_END_TIME as PP_END_TIME,
from PP_RUNDATA PP_RUNDATA
The time columns are in seconds. This PL/SQL converts the seconds into date / time. (this work well)
alter session set nls_date_format="dd/mm/yyyy - hh24:mi:ss";
select date '1970-01-01' + 1661596871 * interval '1' second result from dual;
I need to bring together the SQL and PL/SQL and change the 1661596871 to the column value.
I know I can use a "List Value" to process the PP_START_TIME / PP_END_TIME column using PL/SQL returning SQL.
A very poor, not working example,
declare
temp1 varchar2(500);
begin
EXECUTE IMMEDIATE 'alter session set nls_date_format="dd/mm/yyyy - hh24:mi:ss"';
select date '1970-01-01' + PP_START_TIME * interval '1' second into temp1 from dual;
return 'select temp2 from dual';
End;
I know there are more than two things wrong with this coding;
1) Syntax to access data in column PP_START_TIME (maybe &PP_START_TIME.)
2) the "return statement"
What am I doing wrong in the coding / my thinking in the above or maybe the overall approach in Oracle APEX Reports?
Thanks for looking
Pete
You can use:
select ID,
DATE '1970-01-01' + PP_START_TIME * INTERVAL '1' SECOND as PP_START_TIME,
DATE '1970-01-01' + PP_END_TIME * INTERVAL '1' SECOND as PP_END_TIME
from PP_RUNDATA
If you want a particular format then you can use TO_CHAR:
select ID,
TO_CHAR(
DATE '1970-01-01' + PP_START_TIME * INTERVAL '1' SECOND,
'dd/mm/yyyy - hh24:mi:ss'
) as PP_START_TIME,
TO_CHAR(
DATE '1970-01-01' + PP_END_TIME * INTERVAL '1' SECOND,
'dd/mm/yyyy - hh24:mi:ss'
) as PP_END_TIME
from PP_RUNDATA

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

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

Migrating Oracle query to PostgreSQL

Can you please help me with this? How can I convert below query to PostgreSQL.
The query below gives different output when executed in PostgreSQL than when executed in Oracle.
SELECT
to_char(to_date('01011970','ddmmyyyy') + 1/24/60/60 * 4304052,'dd-mon-yyyy hh24:mi:ss')
from dual;
Let's assume you want to use the same expression as in Oracle to compute the resulting value.
The reason it is not working when you simply remove from dual is because this expression is being evaluated to 0 as integer division truncates results towards 0.
select 1/24/60/60 * 4304052;
?column?
----------
0
(1 row)
If I make one of them a decimal, it will give you the required result
select 1.0/24/60/60 * 4304052;
?column?
-----------------------------
49.815416666666666347848000
Now, after changing this, your expression will return the same result you got in Oracle.
SELECT to_char( to_date('01011970','ddmmyyyy')
+ INTERVAL '1 DAY' * (1.0/24/60/60 * 4304052) ,'dd-mon-yyyy hh24:mi:ss') ;
to_char
----------------------
19-feb-1970 19:34:12
(1 row)
Note that I had to add an interval expression, because unlike Oracle, a Postgres DATE does not store time component and simply adding a number to date will result in an error. Using an interval will ensure that it will be evaluated as timestamp.
knayak=# select pg_typeof( current_date);
pg_typeof
-----------
date
(1 row)
knayak=# select pg_typeof( current_date + INTERVAl '1 DAY');
pg_typeof
-----------------------------
timestamp without time zone
(1 row)
I think you want:
select '1970-01-01'::date + 4304052 * interval '1 second';
You can use to_char() to convert this back to a string, if you really want:
select to_char('1970-01-01'::date + 4304052 * interval '1 second', 'YYYY-MM-SS HH24:MI:SS');

Using "Interval" in Oracle where "Interval" is a value from a table

I need to generate a list of values in an Oracle DB with the following columns of data:
ITEM_TYPE VARCHAR2(20)
ITEM_LAST_UPDATED DATE
ITEM_UPDATE_TOLERANCE NUMBER(1)
The only data that should be send out to the console would be items that have the date in 'ITEM_LAST_UPDATED' less than the sysdate minus the integer value within 'ITEM_UPDATE_TOLERANCE'.
So, if I wanted to just show the ones that were one hour past due, I can do:
select ITEM_TYPE from MY_TABLE
where
to_char(ITEM_LAST_UPDATED, 'DD-MON-YYYY HH24:MI')
<=
to_char(sysdate - interval '1' hour, 'DD-MON-YYYY HH24:MI');
However, rather than using the '1' in the above statement, I need to replace it with the numeric value of ITEM_UPDATE_TOLERANCE.
I tried several different versions, but all error (such as):
select ITEM_TYPE from MY_TABLE
where
to_char(ITEM_LAST_UPDATED, 'DD-MON-YYYY HH24:MI')
<=
to_char(sysdate - interval to_number(ITEM_UPDATE_TOLERANCE) hour, 'DD-MON-YYYY HH24:MI');
Why are you converting a perfect DATE column to a character value just to compare it another DATE value converted to a character column.
Simply use:
ITEM_LAST_UPDATED <= sysdate - interval '1' hour
To achieve what you want, just multiply the value:
ITEM_LAST_UPDATED <= sysdate - (interval '1' hour) * ITEM_UPDATE_TOLERANCE
There is also absolutely no need to convert a number to a number using the to_number() function.
As an alternative to #a_horse_with_no_name's interval multiplication trick, or San's division method, you can also use the numtodsinterval() function:
ITEM_LAST_UPDATED <= sysdate - numtodsinterval(ITEM_UPDATE_TOLERANCE, 'HOUR')
As an example:
select sysdate, sysdate - numtodsinterval(3, 'HOUR') from dual;
SYSDATE SYSDATE-NUMTODSINTE
------------------- -------------------
2014-03-07 19:08:27 2014-03-07 16:08:27
Well you can try using simple calculation
select ITEM_TYPE from MY_TABLE
where
ITEM_LAST_UPDATED
<=
sysdate - (ITEM_UPDATE_TOLERANCE/24);
Calculation of ITEM_UPDATE_TOLERANCE/24 will convert hours into days and then can be subtracted from sysdate.