I want to take difference of 2 date fields but both are varchar field
SyntaxEditor Code Snippet :
start_time- 2018-03-02 06:31:22
end_time - 2018-03-02 06:33:32.478000
I want the result in integer as 2 in min always and it should be an integer always
Result :- 2
It would be very great if anyone can help to achieve my case.
Thanks
You can utilize interval calculations like cast((cast(end_time as timestamp) - cast(start_time as timestamp) minute(4)) as int), but it will fail for > 9999 minutes.
This is SQL UDF for calculating the difference of timestamps in seconds without limitations:
REPLACE FUNCTION TimeStamp_Diff_Seconds
(
ts1 TIMESTAMP(6)
,ts2 TIMESTAMP(6)
)
RETURNS DECIMAL(18,6)
LANGUAGE SQL
CONTAINS SQL
RETURNS NULL ON NULL INPUT
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
(CAST((CAST(ts2 AS DATE)- CAST(ts1 AS DATE)) AS DECIMAL(18,6)) * 60*60*24)
+ ((EXTRACT( HOUR FROM ts2) - EXTRACT( HOUR FROM ts1)) * 60*60)
+ ((EXTRACT(MINUTE FROM ts2) - EXTRACT(MINUTE FROM ts1)) * 60)
+ (EXTRACT(SECOND FROM ts2) - EXTRACT(SECOND FROM ts1))
;
If you can't create UDFs you can copy the source and apply a final / 60 and cast it as integer.
Related
I would like to get difference between two columns (both TT_TIMESTAMP(26,6)) select timestamp1 - timestamp2 as diff from table; but getting this error: An interval data type must be specified for a datetime arithmetic result
Any ideas?
In T-SQL you would need to provide an internal type (year,month,day ect) that would identify the specific element that you are wanting to se the differance in.
A formula like the following should give you the differance in days for example in T-SQL:
DATEDIFF(day, timestamp1, timestamp2) AS DateDiff
W3 Schools has a good indicator of the various options you can use
TimestTen 18.1 Documentation Reference
For TimesTen Database,
The difference between the two TT_TIMESTAMP datatype columns results into an INTERVAL datatype. (Not TT_TIMESTAMP)
And to get the desired component of the INTERVAL datatype, we must use EXTRACT function.
Below is one example.
-- Data preparation
CREATE TABLE DEMO (A TT_TIMESTAMP, B TT_TIMESTAMP;
INSERT INTO DEMO VALUES (TT_TIMESTAMP '2022-01-01 01:01:01.000000', TT_TIMESTAMP '2022-01-05 01:01:01.000000');
-- Below will return an error (as expected)
SELECT B-A FROM DEMO;
2789: An interval data type must be specified for a datetime arithmetic result
The command failed.
So, for the actual difference, we need to calculate like below.
-- Extract data like below
SELECT EXTRACT(DAY FROM B-A) FROM DEMO;
< 4 >
SELECT EXTRACT(HOUR FROM B-A) FROM DEMO;
< 0 >
SELECT EXTRACT(MINUTE FROM B-A) FROM DEMO;
< 0 >
SELECT EXTRACT(SECOND FROM B-A) FROM DEMO;
< 0 >
-- Get SECONDS between two TT_TIMESTAMP columns
SELECT
(EXTRACT(DAY FROM B-A) * 24 * 3600
+ EXTRACT(HOUR FROM B-A) * 3600
+ EXTRACT(MINUTE FROM B-A) * 60
+ EXTRACT(SECOND FROM B-A))
FROM
demo;
< 345600 >
Thanks to previous question...
I have a more simplified OR statement.
Question is instead of a IN how could I change this to a between?
TO_DATE(TO_CHAR(TO_DATE(''19700101'',''yyyymmdd'') + + (FLOOR(ph.change_date/24/60/60)))) IN (''23-DEC-2020'', ''29-DEC-2020'')
So I want to say between the 23-DEC-2020 and 29-DEC-2020 including both?
Thanks
If you want to use an index on the change_date column then perform the conversion on the literal values and convert them to epoch times (rather than converting the column's epoch time to a date, which would not allow you to use a normal index on the column):
ph.change_date BETWEEN ( DATE '2020-12-23' - DATE '1970-01-01' ) * 86400
AND ( DATE '2020-12-29' - DATE '1970-01-01' ) * 86400 + 86399
change_date seems to be in Unix timestamp format -- the number of seconds since 1970-01-01. I would recommend doing the comparison by converting constant values to the same format:
where ph.change_date >= (date '2020-12-23' - date '1970-01-01') * 24 * 60 * 60 and
ph.change_date < (date '2020-12-30' - date '1970-01-01') * 24 * 60 * 60
Note that this is index (and partition) friendly. And, the second comparison is < on the next day to get the entire day.
If you need to deal with the column as "real" dates, you can add a computed column
alter table t add column change_date_date date generated always as
(cast(date '1970-01-01' as timestamp) + change_date * interval '1' second);
You can then reference change_date_date and even define a an index on it.
You can do it with the between keyword.
For example:
to_date('2021.01.06', 'yyyy.mm.dd') between to_date('2021.01.01', 'yyyy.mm.dd') and to_date('2021.01.31', 'yyyy.mm.dd')
I think we can try like this
where <expression>
between TO_DATE('23-DEC-2020','DD-MON-YYYY') and
TO_DATE('29-DEC-2020','DD-MON-YYYY')
You can convert the epoch time(ph.change_date) to date and then compare as follows:
Date'1970-01-01' + FLOOR(ph.change_date/24/60/60) -- considering that change_date is epoch time
between date'2020-12-23' and date'2020-12-23'
I am getting an ORA-01873 "leading precision of the interval is too small" error from this statement and can't figure out why:
The v_not_auto_bl_num is declared as VARCHAR2(1000).
What is causing the error?
In the code you originally posted you are doing:
ABS( EXTRACT(DAY FROM (TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF')
- TO_TIMESTAMP(DHS.COMPLETED_IODT,'YYYYMMDDHH24MISS.FF')) *86400*1000) / 1000)
The relevant part is this:
(TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF')
- TO_TIMESTAMP(DHS.COMPLETED_IODT,'YYYYMMDDHH24MISS.FF')) *86400*1000
If you subtract two timestamps you get an interval data type, not a number; e.g. if your table columns were, say, '20170419065416' and '20170419000000' then subtracting them would generate:
(TO_TIMESTAMP(DHS.A
-------------------
+00 06:54:16.000000
If you multiply that by 86400*1000 you exceed the precision of the interval data type. I chose that value because one second less is OK:
with dhs (assignment_iodt, completed_iodt) as (
select '20170419065415', '20170419000000' from dual
)
select (TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF')
- TO_TIMESTAMP(DHS.COMPLETED_IODT,'YYYYMMDDHH24MISS.FF')) as original,
(TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF')
- TO_TIMESTAMP(DHS.COMPLETED_IODT,'YYYYMMDDHH24MISS.FF')) *86400*1000 as multiplied
from dhs;
ORIGINAL MULTIPLIED
------------------- -------------------------
+00 06:54:15.000000 +24855000 00:00:00.000000
Once second more (or, in fact, anything beyond 20170419065415.134814814, or any pair of values with the actual interval above 06:54:15.134814814) will error as the multiplied interval is out of range for the data type.
What's actually happening under the hood is unclear; using a smaller multiplier also causes the issues once you cross that raw interval size limit.
Anyway, you seem to be trying to get the number of while seconds, which you can do by extracting each time element and multiplying them individually:
select abs(
(extract(day from diff) * 86400)
+ (extract (hour from diff) * 3600)
+ (extract (minute from diff) * 60)
+ trunc(extract (second from diff))
) as c_f_previous_time
from (
select to_timestamp(dhs.assignment_iodt,'YYYYMMDDHH24MISS.FF')
- to_timestamp(dhs.completed_iodt,'YYYYMMDDHH24MISS.FF') as diff
from dhs
);
I've put the timestamp subtraction in an inline view just so it doesn't have to be repeated within each extract call. You can put the rest of your original query inside that inline view (or a CTE) too.
Incidentally, the abs() implies you can have rows in your table where the completed date is earlier than the assignment; or just that you didn't notice you're doing the subtraction the wrong way round. If you data cannot have completed before assigned then you can swap the terms over and lose the abs(); I'd probably swap the terms anyway just to make it look more logical.
first try this:
create table test_table as
SELECT ACT_BL.BL_NUM,
ABS( EXTRACT(DAY FROM (TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF') - TO_TIMESTAMP(DHS.COMPLETED_IODT,'YYYYMMDDHH24MISS.FF')) *86400*1000) / 1000) AS C_F_PREVIOUS_TIME
FROM DOCI_ACTIVITY ACT ,
DOCI_ACTIVITY_RELATED_BL ACT_BL ,
DSH_ACTIVITY DHS
WHERE ACTIVITY_TYPE IN ('BlCodingAndFormatting','BlCreationFromESI')
AND ACT.ACTIVITY_ID =ACT_BL.ACTIVITY_ID
AND ACT_BL.ACTIVITY_ID = DHS.ACTIVITY_ID
AND ACT_BL.BL_NUM = v_not_auto_bl_num;
then check the test_table columns type(BL_NUM and C_F_PREVIOUS_TIME)
after that you apply that column types to your table
In your case, the exception is raised when you multiply an interval by 86400.
As I've posted here you could use the following shorter method to convert interval to milliseconds.
SELECT ROUND((EXTRACT(DAY FROM (
TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF') - TO_TIMESTAMP(DHS.COMPLETED_IODT ,'YYYYMMDDHH24MISS.FF')
) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
TO_TIMESTAMP(DHS.ASSIGNMENT_IODT,'YYYYMMDDHH24MISS.FF') - TO_TIMESTAMP(DHS.COMPLETED_IODT ,'YYYYMMDDHH24MISS.FF')
))) * 1000) AS MILLIS FROM DUAL;
Your numeric number appears to be too large for the ABS function to handle. The biggest value you can pass to ABS() as the number is 2^31-1:
Is there a way how to directly convert value in milliseconds (e.g. 1480515430991) to Oracle TIMESTAMP(6) column? Like some pattern I'm missing for the TO_TIMESTAMP or TO_DATE functions?
All I could find so far are some calculations with intervals and to_date('1970-01-01','YYYY-MM-DD') or other crazy "manual" calculations.
Thanks
EDIT:
Thanks guys. I didn't ask how to do the conversion though. I asked if there is a direct (native, more straightforward) way to achieve it and avoid these calculations for a given input. I am just a curious person and there are many undocumented features out there (Oracle not excluded). I guess NO is my answer then.
Correct function, i.e. include time zone consideration and milliseconds would be this one (using literals):
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return (TIMESTAMP '1970-01-01 00:00:00 UTC' + epoch/1000 * INTERVAL '1' SECOND) AT LOCAL;
end;
/
This is how to get timestamp from epoch.:
select to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch from dual;
So in insert please insert to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch instead of epoch. You can also create function for that:
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch;
end;
/
And then operate on function. These are not "crazy manual calculations" just a legal way of conversion.
If you want to express the unix time, the time since epoch, you need seconds, not milliseconds. See Unixtime
Oracle's datatype TIMESTAMP is in fractional seconds, as you can read in the Oracle documentation. Link zu 11g documentation
Year, month, and day values of date, as well as hour, minute, and second values of time, where fractional_seconds_precision is the number of digits in the fractional part of the SECOND datetime field. Accepted values of fractional_seconds_precision are 0 to 9.
And to answer your question: there is a TO_TIMESTAMP function. See 11g documentation
The TO_TIMESTAMP function converts text data to a value of TIMESTAMP data type.
You can use it like this
TO_TIMESTAMP('2016/11/30 15:53:18', 'YYYY/MM/DD HH:MI:SS')
and would get '30-NOV-16 15.53.18.000000000 AM'
If, for some reason, you really need to display the seconds sind epoch, you can use the noncrazy calculation
select (SYSDATE - to_date('1970-01-01', 'yyyy-MM-dd')) * 24 * 60 * 60 from dual;
SELECT cast(current_date as timestamp(2));
SELECT ('1970-01-01 00:00:00' (timestamp(2)));
I Need to get the number of seconds between the current_date ,1970-01-01 00:00:00
with SQL.
These are some SQL UDFs, if you're not on TD13.10 you might just use the calculation instead:
/**********
Converting Unix/POSIX time to a Timestamp
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
Also working for negative numbers.
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION UnixTime_to_TimeStamp (UnixTime INT)
RETURNS TIMESTAMP(0)
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
CAST(DATE '1970-01-01' + (UnixTime / 86400) AS TIMESTAMP(0))
+ ((UnixTime MOD 86400) * INTERVAL '00:00:01' HOUR TO SECOND)
;
SELECT
UnixTime_to_TimeStamp(-2147483648)
,UnixTime_to_TimeStamp(0)
,UnixTime_to_TimeStamp(2147483647)
;
/**********
Converting a Timestamp to Unix/POSIX time
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION TimeStamp_to_UnixTime (ts TIMESTAMP(6))
RETURNS INTEGER
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
(CAST(ts AS DATE) - DATE '1970-01-01') * 86400
+ (EXTRACT(HOUR FROM ts) * 3600)
+ (EXTRACT(MINUTE FROM ts) * 60)
+ (EXTRACT(SECOND FROM ts))
;
SELECT
TimeStamp_to_UnixTime(TIMESTAMP '1901-12-13 20:45:52')
,TimeStamp_to_UnixTime(CURRENT_TIMESTAMP)
,TimeStamp_to_UnixTime(TIMESTAMP '2038-01-19 03:14:07')
;
/**********
Difference between two Timestamps in seconds
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION TimeStamp_Diff_Seconds
(
ts1 TIMESTAMP(6)
,ts2 TIMESTAMP(6)
)
RETURNS DECIMAL(18,6)
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
(CAST((CAST(ts2 AS DATE AT 0)- CAST(ts1 AS DATE AT 0)) AS DECIMAL(18,6)) * 86400)
+ ((EXTRACT( HOUR FROM ts2) - EXTRACT( HOUR FROM ts1)) * 3600)
+ ((EXTRACT(MINUTE FROM ts2) - EXTRACT(MINUTE FROM ts1)) * 60)
+ (EXTRACT(SECOND FROM ts2) - EXTRACT(SECOND FROM ts1))
;
SELECT
TimeStamp_Diff_Seconds(TIMESTAMP '9999-12-31 23:59:59'
,TIMESTAMP '0001-01-01 00:00:00')
,TimeStamp_Diff_Seconds(CURRENT_TIMESTAMP
,TIMESTAMP '1970-01-01 00:00:00')
;