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 >
Related
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 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
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.
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:
I have a table with a start time timestamp column and a length integer column representing minutes. I want to be able to do something like this:
SELECT * FROM table t
WHERE t.start_time + interval 't.length minutes' < '2011-10-21';
But this doesn't work. Is there any way to get a new timestamp from the two column values?
I'm running version 8.3 of postgresql
SELECT *
FROM table
WHERE (start_time + interval '1 min' * length_minutes) < '2011-10-21 0:0'::timestamp;
Notes
Simply multiply your integer with 1-minute intervals and add it to the timestamp.
It is slightly faster to compare the timestamp to a timestamp. A date would have to be cast to timestamp (automatically).
You need to cast t.length as a character and then append it... try this instead?
SELECT *
FROM table t
WHERE
t.start_time
+ cast(cast(t.length as character varying) || ' minutes' as interval)
< '2011-10-21';