My output is like this
**Column1** **Column2**
20170123012057.555 20170123070616.314
I should get 20719 seconds if I minus the two columns
You can convert the values to a date and then take the difference:
select (to_date(substr(column2, 1, 14), 'YYYYMMDDHH24MISS') -
to_date(substr(column1, 1, 14), 'YYYYMMDDHH24MISS')
) * 24 * 60 * 60
Try this. Here I have given difference in milliseconds as well as in seconds which is rounded
select
extract(day from (to_timestamp(column2,'YYYYMMDDHH24MISS.FF')
-to_timestamp(column1,'YYYYMMDDHH24MISS.FF')
)*86400*1000) / 1000
as diff_in_milliseconds
,round(extract(day from (to_timestamp(column2,'YYYYMMDDHH24MISS.FF')-
to_timestamp(column1,'YYYYMMDDHH24MISS.FF')
)*86400*1000) / 1000
) as diff_in_seconds
from
(select
'20170123012057.555' as Column1,
'20170123070616.314' as Column2
from dual)
Output
DIFF_IN_MILLISECONDS DIFF_IN_SECONDS
20718,7590 20719
Try as
SELECT ROUND (seconds / 1000) seconds
FROM (SELECT EXTRACT (DAY FROM cal_col) * 24 * 60 * 60 * 1000
+ EXTRACT (HOUR FROM cal_col) * 60 * 60 * 1000
+ EXTRACT (MINUTE FROM cal_col) * 60 * 1000
+ ROUND (EXTRACT (SECOND FROM cal_col) * 1000)
seconds
FROM (SELECT TO_TIMESTAMP ('20170123070616.314',
'YYYYMMDDHH24MISS.FF')
- TO_TIMESTAMP ('20170123012057.555',
'YYYYMMDDHH24MISS.FF')
cal_col
FROM DUAL))
Related
Is there a way in Oralce PL/SQL to get the current timestamp as a NUMBER ?
Something like '1582185277302'
You can convert the current date/time to an epoch timestamp as follows:
(sysdate - date '1970-01-01') * 60 * 60 * 24
This gives you the number of seconds since January 1st, 1970.
If you want the results in milliseconds, then:
(cast(systimestamp as date) - date '1970-01-01') * 24 * 60 * 60 * 1000
+ mod( extract(second from systimestamp), 1) * 1000
Unix timestamps are in the UTC time zone. If your timestamp is in the UTC time zone then you can use:
( TRUNC( your_timestamp, 'MI' ) - DATE '1970-01-01' ) * 24 * 60 * 60 * 1000
+ EXTRACT( SECOND FROM your_timestamp ) * 1000
If your timestamps have a time zone then:
( TRUNC( your_timestamp AT TIME ZONE 'UTC', 'MI' ) - DATE '1970-01-01' ) * 24 * 60 * 60 * 1000
+ EXTRACT( SECOND FROM your_timestamp ) * 1000
If you need to handle leap seconds then you can use this answer.
If you subtract a fixed timestamp from systimestamp you get an interval:
select systimestamp - timestamp '1970-01-01 00:00:00 UTC' as diff
from dual;
DIFF
----------------------
+18312 10:05:29.674905
You can then extract and manipulate the elements of that to get the epoch time, using an inline view or CTE to avoid repeating the interval generation:
with t (diff) as (
select systimestamp - timestamp '1970-01-01 00:00:00 UTC'
from dual
)
select trunc(1000 * (
extract (day from diff) * 24 * 60 *60
+ extract (hour from diff) * 60 * 60
+ extract (minute from diff) * 60
+ extract (second from diff)
)) as epoch
from t;
EPOCH
-------------------
1582193129829
Adding all the extracts together gives you the total number of seconds, including fractions; multiplying by 1000 gives you milliseconds but will still have fractions (depending on the precision your platform supports); truncating that gives you just milliseconds.
This takes the time zone into account (epoch times are supposed to count from UTC); but doesn't allow for leap seconds.
As a further demo, using a fixed time in an arbitrary time zone to get the result in your question:
with t (diff) as (
select timestamp '2020-02-20 02:54:37.302789 America/New_York' - timestamp '1970-01-01 00:00:00 UTC'
from dual
)
select trunc(1000 * (
extract (day from diff) * 24 * 60 *60
+ extract (hour from diff) * 60 * 60
+ extract (minute from diff) * 60
+ extract (second from diff)
)) as epoch
from t;
EPOCH
-------------------
1582185277302
I need convert a millisecond value, 85605304.3587 to a value like 0d 18h 21m.
No idea on how to start that, is there something similar to a TimeSpan in SQL like there is in C#?
You can do the calculation explicitly. I think it is:
select floor(msvalue / (1000 * 60 * 60 * 24)) as days,
floor(msvalue / (1000 * 60 * 60)) % 24 as hours,
floor(msvalue / (1000 * 60)) % 60 as minutes
Note: Some databases use mod instead of %.
In MS SQL SERVER you can use next code:
with cte as (
select cast(85605304.3587 as int) / 1000 / 60 as [min]
), cte2 as (
select
cast([min] % 60 as varchar(max)) as minutes,
cast(([min] / 60) % 24 as varchar(max)) as hours,
cast([min] / (60 * 24) as varchar(max)) as days
from cte
)
select concat(days, 'd ', hours, 'h ', minutes, 'm') as tm
from cte2
Using native date & time functions, maybe:
SELECT
AsDateTime = DATEADD(MILLISECOND, 85605304, 0)
, AsDateTime2 = DATEADD(NANOSECOND, 7 * 100, DATEADD(MICROSECOND, 358, DATEADD(MILLISECOND, 85605304, CONVERT(datetime2, CONVERT(datetime, 0)))))
-- Incorrect datetime2 approach I initially did, has some precision loss, probably due to datetime's millisecond issue with 0's, 3's, and 7.'s
--SELECT DontDoThis = DATEADD(NANOSECOND, 7 * 100, DATEADD(MICROSECOND, 358, CONVERT(datetime2, DATEADD(MILLISECOND, 85605304, 0))))
datetime covers only 3 digits beyond seconds, while datetime2 will maintain 7 digits. Perhaps other ways that give date-like objects exist, I wouldn't know.
I have this time-duration: 00:00:23.323
I want to convert it in sql to milliseconds.
EDIT://
I tried this but it isn't very nice:
SELECT (DATEPART(hh,'12:13:14.123') * 60 * 60 * 1000)
SELECT (DATEPART(n,'12:13:14.123') * 60 * 1000)
SELECT (DATEPART(s,'12:13:14.123') * 1000)
SELECT DATEPART(ms,'12:13:14.123')
How does it work?
Thanks for your answers.
Use DATEDIFF:
SELECT DATEDIFF(MILLISECOND, 0, '00:00:23.323')
Result:
23323
You can use datepart function.
like this
select DATEPART(MILLISECOND,GETDATE())+DATEPART(second,getdate())*1000
I got it, it isn't the nice way but it works:
SELECT (DATEPART(hh,'00:00:23.323') * 60 * 60 * 1000) + (DATEPART(n,'00:00:23.323') * 60 * 1000) + (DATEPART(s,'00:00:23.323') * 1000) + DATEPART(ms,'00:00:23.323') AS 'DurationInMillis'
When subtracting timestamps the return value is an interval data-type. Is there an elegant way to convert this value into the total number of (milli/micro) seconds in the interval, i.e. an integer.
The following would work, but it's not very pretty:
select abs( extract( second from interval_difference )
+ extract( minute from interval_difference ) * 60
+ extract( hour from interval_difference ) * 60 * 60
+ extract( day from interval_difference ) * 60 * 60 * 24
)
from ( select systimestamp - (systimestamp - 1) as interval_difference
from dual )
Is there a more elegant method in SQL or PL/SQL?
An easy way:
select extract(day from (ts1-ts2)*86400) from dual;
The idea is to convert the interval value into days by times 86400 (= 24*60*60).
Then extract the 'day' value which is actually second value we wanted.
I hope this help:
zep#dev> select interval_difference
2 ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
3 from (select systimestamp - (systimestamp - 1) as interval_difference
4 from dual)
5 ;
INTERVAL_DIFFERENCE FRACT_SEC_DIFFERENCE
------------------------------------------------------------------------------- --------------------
+000000001 00:00:00.375000 86400,375
With your test:
zep#dev> select interval_difference
2 ,abs(extract(second from interval_difference) +
3 extract(minute from interval_difference) * 60 +
4 extract(hour from interval_difference) * 60 * 60 +
5 extract(day from interval_difference) * 60 * 60 * 24) as your_sec_difference
6 ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
7 ,round(sysdate + (interval_difference * 86400) - sysdate) as sec_difference
8 ,round((sysdate + (interval_difference * 86400) - sysdate) * 1000) as millisec_difference
9 from (select systimestamp - (systimestamp - 1) as interval_difference
10 from dual)
11 /
INTERVAL_DIFFERENCE YOUR_SEC_DIFFERENCE FRACT_SEC_DIFFERENCE SEC_DIFFERENCE MILLISEC_DIFFERENCE
------------------------------------------------------------------------------- ------------------- -------------------- -------------- -------------------
+000000001 00:00:00.515000 86400,515 86400,515 86401 86400515
zep#dev>
I've found this to work. Apparently, if you do arithmetics with timestamps they are converted to some internal datatype that, when substracted from each other, returns the interval as a number.
Easy? Yes. Elegant? No. Gets the work done? Oh yeah.
SELECT ( (A + 0) - (B + 0) ) * 24 * 60 * 60
FROM
(
SELECT SYSTIMESTAMP A,
SYSTIMESTAMP - INTERVAL '1' MINUTE B
FROM DUAL
);
Unfortunately, I don't think that there is an alternative (or more elegant) way of calculating total seconds from an interval type in pl/sql. As this article mentions:
... unlike .NET, Oracle provides no simple equivalent to TimeSpan.TotalSeconds.
therefore extracting day, hour etc from the interval and multiplying them with corresponding values seems like the only way.
Based on zep's answer, I wrapped things up into a function for your convenience:
CREATE OR REPLACE FUNCTION intervalToSeconds(
pMinuend TIMESTAMP , pSubtrahend TIMESTAMP ) RETURN NUMBER IS
vDifference INTERVAL DAY TO SECOND ;
vSeconds NUMBER ;
BEGIN
vDifference := pMinuend - pSubtrahend ;
SELECT EXTRACT( DAY FROM vDifference ) * 86400
+ EXTRACT( HOUR FROM vDifference ) * 3600
+ EXTRACT( MINUTE FROM vDifference ) * 60
+ EXTRACT( SECOND FROM vDifference )
INTO
vSeconds
FROM DUAL ;
RETURN vSeconds ;
END intervalToSeconds ;
Use following query:
select (cast(timestamp1 as date)-cast(timestamp2 as date))*24*60*60)
Similar to #Zhaoping Lu answer but directly extracting seconds instead of getting them from the number of days.
SELECT extract(second from (end_date - start_date)) as "Seconds number"
FROM my_table
(worked on PostgresSQL 9.6.1)
A shorter method to convert timestamp to nanoseconds.
SELECT (EXTRACT(DAY FROM (
SYSTIMESTAMP --Replace line with desired timestamp --Maximum value: TIMESTAMP '3871-04-29 10:39:59.999999999 UTC'
- TIMESTAMP '1970-01-01 00:00:00 UTC') * 24 * 60) * 60 + EXTRACT(SECOND FROM
SYSTIMESTAMP --Replace line with desired timestamp
)) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to timestamp.
SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS TIMESTAMP FROM dual;
TIMESTAMP
26/08/20 09:33:47,263027000 UTC
As expected, above methods' results are not affected by time zones.
A shorter method to convert interval to nanoseconds.
SELECT (EXTRACT(DAY FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval --Maximum value: INTERVAL '+694444 10:39:59.999999999' DAY(6) TO SECOND(9) or up to 3871 year
) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval
))) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to interval.
SELECT numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS INTERVAL FROM dual;
INTERVAL
+18500 09:33:47.263027
Replace 1000000000 by 1000, for example, if you'd like to work with milliseconds instead of nanoseconds.
I've tried some of posted methods, but a got the exception "ORA-01873: the leading precision of the interval is too smalll" when multiplying the interval by 86400, so I've decided do post the methods that works for me.
SELECT to_char(ENDTIME,'yyyymmddhh24missff')-to_char(STARTTIME,'yyyymmddhh24missff') AS DUR
FROM DUAL;
yyyymmddhh24miss- WILL GIVE DURATION IN SEC
yyyymmddhh24mi DURATION IN MIN
yyyymmddhh24 - DURATION - HOURS
yyyymmdd DURATION IN DAYS
I need a query that displays dates in the following format:
Dates that fall in the past 7 days -> “one week ago”
Dates that fall in the past 7 to 14 days -> “two week ago”
Etc…
Dates that fall in the past 30 days -> “one month ago”
Dates that follow in the past 30 to 60 days -> “two months ago
Etc..
Dates that fall in the past 365 days -> “one year ago”
Dates that fall in the past 365 to 730 days -> “two years ago
Etc...
If you guys can point me to the right direction I’ll appreciate it.
Thank you
Here is a mysql function I wrote called time_ago
DELIMITER $$
DROP FUNCTION IF EXISTS time_ago;
CREATE FUNCTION time_ago (ts datetime)
RETURNS varchar(255)
DETERMINISTIC
BEGIN
DECLARE utx INT SIGNED DEFAULT 1;
DECLARE nowutx INT SIGNED DEFAULT 1;
DECLARE dif INT SIGNED DEFAULT 1;
DECLARE method varchar(255);
DECLARE cnt varchar(255);
DECLARE plural tinyint(11);
DECLARE future tinyint(11);
SET utx := UNIX_TIMESTAMP(ts);
SET nowutx := UNIX_TIMESTAMP(NOW());
SET future := utx > nowutx;
SET dif := IF(future, utx - nowutx, nowutx - utx);
SET method := IF(dif < 60, 'Second', IF(
dif < (60 * 60), 'Minute', IF(
dif < (60 * 60 * 24), 'Hour', IF(
dif < (60 * 60 * 24 * 7), 'Day' , IF(
dif < (60 * 60 * 24 * 365), 'Week', 'Year')))));
SET cnt := IF(dif < 60, dif, IF(
dif < (60 * 60), floor(dif / 60), IF(
dif < (60 * 60 * 24), floor(dif / (60 * 60)), IF(
dif < (60 * 60 * 24 * 7), floor(dif / (60 * 60 * 24)) , IF(
dif < (60 * 60 * 24 * 365) , floor(dif / (60 * 60 * 24 * 7)), floor(dif / (60 * 60 * 24 * 365)))))));
SET plural := cnt != 1;
return CONCAT(IF(future, 'In ', ''), cnt, ' ',method, IF(plural, 's', '') , IF(future, ' From Now', ' Ago'));
END$$
DELIMITER ;
It is used like this
SELECT time_ago(date_ordered) time_ago FROM orders LIMIT 1
And the result looks like this:
time_ago
22 Weeks Ago
EDIT:
I have modified this answer to offer an improved version of this function: the new version uses DECLARE instead of setting session variables.
As stated above, use a case statement in your SQL query. Something like this:
SELECT
Column1,
Column2,
theDate,
CASE
WHEN DATEDIFF(dd, theDate, GetDate()) =< 7 THEN 'One Week Ago'
WHEN DATEDIFF(dd, theDate, GetDate()) > 7 AND DATEDIFF(dd, theDate, GetDate()) < 30 THEN 'One Month Ago'
-- ...
END
AS TimeAgo,
Column3,
Column4
FROM Table1
More Information for MS SQL: http://msdn.microsoft.com/en-us/library/ms181765.aspx
(Or see the documentation for your SQL server brand)