Migrating Oracle query to PostgreSQL - sql

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');

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

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;

postgres convert a substring to epoch

I'm trying to convert a epoch timestamp to a human readable timestamp in a single query but I'm getting a little stuck - any help is appreciated.
testing=# SELECT creation_time FROM users LIMIT 1;
creation_time
---------------
1354006445722
(1 row)
testing=# SELECT SUBSTRING('1498123813330', 1,10);
SUBSTRING
------------
1498123813
(1 row)
testing=# SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 1498123813 * INTERVAL '1 second';
?column?
------------------------
2017-06-22 02:30:13-07
(1 row)
Anyway to put this into a single query?
What you want is CASTing, i.e.
SELECT TIMESTAMP WITH TIME ZONE 'epoch' +
SUBSTRING(creation_time, 1, 10)::NUMERIC * INTERVAL '1 second';
But, if creating time is epoch milliseconds, you could do:
SELECT to_timestamp(creation_time::double precision / 1000)
instead, which will preserve milliseconds too. You can print timestamp out with to_char if you want a format, other than the default timestamp output.
http://rextester.com/EHPNJ86308
Assuming created_time is stored as a varchar, you can apply the same substring logic to it and cast it to a number:
SELECT TIMESTAMP WITH TIME ZONE 'epoch' +
SUBSTRING(creation_time, 1,10)::NUMERIC * INTERVAL '1 second'
FROM users

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.

Confusing result between 'to_date' and 'long to date' in oracle query

I have a table called "subscription" as below.
desc subscription;
Name Null Type
--------------------- -------- ----------
SUBSCRIPTION_ID NOT NULL NUMBER(38)
EXPIRATIONDATE DATE`
And output of the query as below.
SELECT
subscription_id,
expirationdate
FROM subscription
WHERE subscription_id = 41919;
SUBSCRIPTION_ID EXPIRATIONDATE
---------------------- -------------------------
41919 18-JAN-14 13:45:56
And I'm trying to execute following query in different ways.
1st Query returns one row:
SELECT s.subscription_id
FROM subscription$active s
WHERE s.expirationdate - (116 / 24)
BETWEEN TO_DATE('13-JAN-14 11:38:22', 'dd/mm/yyyy hh24:mi:ss')
AND TO_DATE('13-JAN-14 18:30:00', 'dd/mm/yyyy hh24:mi:ss')
AND s.subscription_id = 41919;
SUBSCRIPTION_ID
----------------------
41919
2nd Query returns no rows:
SELECT s.subscription_id
FROM subscription$active s
WHERE s.expirationdate - (116 / 24)
BETWEEN (trunc(1389613102220 / (1000), 0) / (24 * 60 * 60))
+ to_date('01/01/1970', 'mm/dd/yyyy')
AND (trunc(1389637800000 / (1000), 0) / (24 * 60 * 60))
+ to_date('01/01/1970', 'mm/dd/yyyy')
AND s.subscription_id = 41919;
SUBSCRIPTION_ID
----------------
Here both the above where clause are same. 1st one is trying to use "to_date" and 2nd one converts "long to date". But when I see the out put, the first one returns a row and 2nd doesnot return any result.
I couldn't find out what is difference the 'long to date' conversion makes here.
The conversion between long to date is also correct.
select (trunc(1389613102220 / (1000), 0) / (24 * 60 * 60)) + to_date('01/01/1970','mm/dd/yyyy') from dual
Output:
13-JAN-14 11:38:22
And
select (trunc(1389637800000 / (1000), 0) / (24 * 60 * 60)) + to_date('01/01/1970','mm/dd/yyyy') from dual
Output:
13-JAN-14 18:30:00
Can someone help me to understand the difference between the 1st and 2nd query ?
The problem isn't really your epoch-based query, it's the data in your table (and to some extent the way you're constructing your dates for the non-epoch version). You are using 2-digit years and a 4-digit format mask. When you use to_date in your filter you're actually using the year 0014, not 2014; you can see that just by converting the string value, but showing the result with the full four-digit year:
select to_char(to_date('13-JAN-14 11:38:22', 'dd/mm/yyyy hh24:mi:ss'),
'YYYY-MM-DD HH24:MI:SS') as test_date
from dual;
TEST_DATE
-------------------
0014-01-13 11:38:22
The critical part is that you're converting 14 using format model YYYY. As the documentation mentions:
Numeric elements are padded with leading zeros to the width of the
maximum value allowed for the element. For example, the YYYY element
is padded to four digits (the length of '9999')
Although that's mostly talking about to_char in that section, the same applies to to_date. When you do to_date('14', 'YYYY') it's interpreted as to_date('0014', 'YYYY'). You could use RRRR instead, or RR since you're only providing two digits of the year anyway, either of which would give you 2014; but it's better to be explicit.
It looks like you did that during insertion as well, because your first query will only find record 41919 if it's expiration date is also in 0014.
When you use the epoch timestamp conversion you are actually getting 2014, so your record is really not in that range.
To confirm that, specify a format model with the full year in your initial query:
select subscription_id,
to_char(expirationdate, 'YYYY-MM-DD HH24:MI:SS') as expirationdate
from subscription
where subscription_id = 41919;
SUBSCRIPTION_ID EXPIRATIONDATE
--------------- -------------------
41919 0014-01-18 13:45:56
You'll also see no data if you change the date string, or the format model (which only works now because Oracle is sometimes too helpful parsing these), in your first query:
select s.subscription_id from subscription s
where s.expirationdate - (116/24) between TO_DATE('13-JAN-14 11:38:22',
'dd-mon-rr hh24:mi:ss')
and TO_DATE('13-JAN-14 18:30:00', 'dd-mon-rr hh24:mi:ss')
and s.subscription_id=41919;
... which will return no rows if the table date is 0014.
Examine the full output of the explain plan, generated by querying DBMS_Xplan(), and it will show you the values being used internally as a result of evaluating those constants. That might show the presence of an unexpected data type conversion.