I am trying to calculate a difference between systimestamp and a number in Oracle's SQL.
The number format is YYMMDDhhmmss (For example 190903210000). This number is held in a table and is based on 24 hour timezone.
I am trying to calculate a difference that number and system timestamp in seconds.
I have to use a select sentence as below:
SELECT X FROM table
Then I need to calculate a difference (SYSTEMTIMESTAMP - X)
My system timestamp is formatted like 03-SEP-19 06.21.49.817757 PM +00:00
Could you please advise me on the right approach?
To convert a NUMBER to a TIMESTAMP you can use an expression like TO_TIMESTAMP(TO_CHAR(...)). To compute the number of seconds between two timestamps, one solution is to cast both to dates, and substract them : you will get a (decimal) result in days, which can be then converted to seconds.
Consider:
(
CAST(systimestamp AS DATE)
- CAST(TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') AS DATE)
) * 60 * 60 * 24
However, since your numeric representation of the timestamp does not contain fractional seconds (nor timezone), it would probably be simpler to convert directly to a DATE, which would remove the need to CAST it afterwards, hence:
(
CAST(systimestamp AS DATE)
- TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss')
) * 60 * 60 * 24
Demo on DB Fiddle:
SELECT
systimestamp,
190903210000 num,
TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') num_as_timestamp,
(
CAST(systimestamp AS DATE) -
CAST(TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') AS DATE)
) * 60 * 60 * 24 diff
FROM DUAL;
SYSTIMESTAMP | NUM | NUM_AS_TIMESTAMP | DIFF
:---------------------------------- | -----------: | :------------------------------ | ---:
03-SEP-19 09.13.39.989343 PM +01:00 | 190903210000 | 03-SEP-19 09.00.00.000000000 PM | 819
SELECT
systimestamp,
190903210000 num,
TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss') num_as_date,
(
CAST(systimestamp AS DATE)
- TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss')
) * 60 * 60 * 24 diff
FROM DUAL;
SYSTIMESTAMP | NUM | NUM_AS_DATE | DIFF
:---------------------------------- | -----------: | :----------------- | ----------------------------------------:
03-SEP-19 09.20.44.524445 PM +01:00 | 190903210000 | 03-SEP-19 21:00:00 | 1243.999999999999999999999999999999999996
Try this:
select 190903210000 n,
to_timestamp(to_char(190903210000), 'YYMMDDHH24MISS') ts
from dual;
In SQLFiddle and the Oracle documentation.
Related
In Airtable the DateTime Data is correct but if you upload it in the Big Query, it will transform into a Decimal String Style of Numbers. See the table below:
Date
True Date
44493.61666667
2021-10-24T14:48:00
44522.775
2021-11-22T18:36:00
44493.67916666667
2021-10-24T16:18:00
44522.739583333336
2021-11-22T17:45:00
This is the problem I have encountered. Assuming the table name is Airtable. Can you give a suggested query to convert it into the correct and true timestamp?
Actually, I post it also on my blog. Thank you for your suggested answers.
I don't have access to BigQuery -
but I found out that your "Date" value is a division seconds to days (seconds / 86400) since TIMESTAMP '1899-12-30 00:00:00' :
WITH
airtable(date,true_date) AS (
SELECT 44493.61666666667 ,TIMESTAMP '2021-10-24T14:48:00'
UNION ALL SELECT 44522.775 ,TIMESTAMP '2021-11-22T18:36:00'
UNION ALL SELECT 44493.67916666667 ,TIMESTAMP '2021-10-24T16:18:00'
UNION ALL SELECT 44522.739583333336,TIMESTAMP '2021-11-22T17:45:00'
)
SELECT
*
, true_date - "date" AS anchor_ts
, CAST (("date" * 86400) AS INTEGER) AS secs_snc_anchor
FROM airtable;
-- out date | true_date | anchor_ts | secs_snc_anchor
-- out --------------------+---------------------+---------------------+-----------------
-- out 44493.616666666670 | 2021-10-24 14:48:00 | 1899-12-30 00:00:00 | 3844248480
-- out 44522.775000000000 | 2021-11-22 18:36:00 | 1899-12-30 00:00:00 | 3846767760
-- out 44493.679166666670 | 2021-10-24 16:18:00 | 1899-12-30 00:00:00 | 3844253880
-- out 44522.739583333336 | 2021-11-22 17:45:00 | 1899-12-30 00:00:00 | 3846764700
So you could add your "Date" value to TIMESTAMP '1899-12-30 00:00:00' to get your desired value.
In many databases (Oracle, PostgreSQL, Vertica, etc), it would be like in the query below.
In Big Query, it should be:
TIMESTAMP_ADD(TIMESTAMP "1899-12-30 00:00:00", INTERVAL "Date" MINUTE) AS calculated_ts.
And in the second query, I renamed "Date" to dt, as I shy away from using reserved words (a type designator in this case) for database objects.
WITH
airtable(dt,true_date) AS (
SELECT 44493.61666666667 ,TIMESTAMP '2021-10-24T14:48:00'
UNION ALL SELECT 44522.775 ,TIMESTAMP '2021-11-22T18:36:00'
UNION ALL SELECT 44493.67916666667 ,TIMESTAMP '2021-10-24T16:18:00'
UNION ALL SELECT 44522.739583333336,TIMESTAMP '2021-11-22T17:45:00'
)
SELECT
*
, TIMESTAMPADD(ss,CAST ((dt * 86400) AS INTEGER), TIMESTAMP '1899-12-30 00:00:00') AS derived_ts
FROM airtable;
-- out dt | true_date | derived_ts
-- out --------------------+---------------------+---------------------
-- out 44493.616666666670 | 2021-10-24 14:48:00 | 2021-10-24 14:48:00
-- out 44522.775000000000 | 2021-11-22 18:36:00 | 2021-11-22 18:36:00
-- out 44493.679166666670 | 2021-10-24 16:18:00 | 2021-10-24 16:18:00
-- out 44522.739583333336 | 2021-11-22 17:45:00 | 2021-11-22 17:45:00
Can you give a suggested query to convert it into the correct and true timestamp?
Use following expression timestamp_seconds(cast((date - 25569) * 86400 as int))
You can test it with the sample data in your question as in below example
with airtable as (
select 44493.61666666667 date, '2021-10-24T14:48:00' expected_true_date union all
select 44522.775 , '2021-11-22T18:36:00' union all
select 44493.67916666667 , '2021-10-24T16:18:00' union all
select 44522.739583333336, '2021-11-22T17:45:00'
)
select *,
timestamp_seconds(cast((date - 25569) * 86400 as int)) calculated_true_date
from airtable
output
What is the best Oracle sql query to extract date?
input entry - 2020-10-14T07:26:32.661Z ,
expected output - 2020-10-14
If you want a DATE data type where the time component is midnight then:
SELECT TRUNC(
TO_TIMESTAMP_TZ(
'2020-10-14T07:26:32.661Z',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR'
)
) AS truncated_date
FROM DUAL;
Which (depending on your NLS_DATE_FORMAT) outputs:
| TRUNCATED_DATE |
| :------------------ |
| 2020-10-14 00:00:00 |
(Note: a DATE data type has year, month, day, hour, minute and second components. Whatever client program you are using to access the database may choose not to show the time component but it will still be there.)
If you want a YYYY-MM-DD formatted string then:
SELECT TO_CHAR(
TO_TIMESTAMP_TZ(
'2020-10-14T07:26:32.661Z',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR'
),
'YYYY-MM-DD'
) AS formatted_date
FROM DUAL;
| FORMATTED_DATE |
| :------------- |
| 2020-10-14 |
db<>fiddle here
The canonical way is probably trunc():
select trunc(input_entry)
This assumes that input_entry is a date or timestamp value.
EDIT:
If your input is just a string, use string operations:
select substr(input_entry, 1, 10)
You can also readily cast this to a date.
We can generate a datetime array with specified bin-width using:
select generate_series(
timestamp without time zone '2020-10-01 00:00:00', '2020-10-04 00:00:00',
'24 hours') as ts
ts
1 2020-10-01
2 2020-10-02
3 2020-10-03
4 2020-10-04
Is it possible to generate an array of set length i.e. a given number of bins/breaks?
I want to provide a date range and number of equal intervals to divide it into.
From the comments:
I want to provide a date range and number of equal intervals to divide it into
You can use generate_series() with integers, and date arithmetics:
with params as (
select
timestamp '2020-10-01' ts_start,
timestamp '2020-10-04' ts_end,
3 num
)
select ts_start + (ts_end - ts_start) * i / num as ts
from params
cross join lateral generate_series(0, num) s(i)
This splits the given time range into 3 intervals (resulting in a total of 4 timestamps).
Demo on DB Fiddle:
| ts |
| :------------------ |
| 2020-10-01 00:00:00 |
| 2020-10-02 00:00:00 |
| 2020-10-03 00:00:00 |
| 2020-10-04 00:00:00 |
I would like to compute the number of days since 1899/12/30, taking into consideration the number of hours and minutes.
So, for example data in my table:
+--------------------+-------------+
| dob | n_days |
+--------------------+-------------+
|1980-08-09 13:34:10 | 29442.5654 |
|2005-12-15 23:10:00 | 38701.6528 |
|2020-02-26 15:56:00 | 43887.6639 |
+--------------------+-------------+
Query:
SELECT DATE_PART('day', dob -'1899-12-30 00:00:00'::TIMESTAMPTZ) AS n_days
FROM my_date;
Returns only whole day count:
n_days
---------
29441
38701
43887
Consider:
extract(epoch from dob - '1899-12-30 00:00:00'::timestamptz) / 60 / 60 / 24
Rationale:
the timestamp substraction gives you an interval that represents the difference between the timestamps
extract(epoch from ...) turns this to a number of seconds
all that is left to do is divide by the number of seconds that there is in a day
Demo on DB Fiddle:
with t as (
select '1980-08-09 13:34:10'::timestamptz dob
union all select '2005-12-15 23:10:00'::timestamptz
union all select '2020-02-26 15:56:00'::timestamptz
)
select
dob,
extract(epoch from dob - '1899-12-30 00:00:00'::timestamptz) / 60 / 60 / 24 as n_days
from t
dob | n_days
:--------------------- | :-----------------
1980-08-09 13:34:10+01 | 29442.52372685185
2005-12-15 23:10:00+00 | 38701.965277777774
2020-02-26 15:56:00+00 | 43887.66388888889
I have 2 columns
Start_Time (VARCHAR) Duration_in_sec (NUMBER)
12:03:11 220
11:05:33 345
I want to add col2 to col1 to get end time.
How do I do that I tried
Select TO_NUMBER(col1)+col2 as end_time from ABC.
This is giving me error. Can someone help me with right way.
This is the error I am getting which is straight forward but How to get the right number. I need to do some maniculation on col1 but I am not getting how to do that
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
You can convert the start time and duration both to intervals and then add the two intervals. That way you can handle when the duration is more than one day:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( Start_Time, Duration_in_sec ) AS
SELECT '12:03:11', 220 FROM DUAL UNION ALL
SELECT '11:05:33', 345 FROM DUAL UNION ALL
SELECT '23:59:59', 1 FROM DUAL;
Query 1:
SELECT t.*,
( TO_TIMESTAMP( start_time, 'HH24:MI:SS' )
- TO_TIMESTAMP( '00:00:00', 'HH24:MI:SS' )
) + NUMTODSINTERVAL( Duration_in_sec, 'SECOND' ) AS end_time
FROM table_name t
Results:
| START_TIME | DURATION_IN_SEC | END_TIME |
|------------|-----------------|--------------|
| 12:03:11 | 220 | 0 12:6:51.0 |
| 11:05:33 | 345 | 0 11:11:18.0 |
| 23:59:59 | 1 | 1 0:0:0.0 |
A better solution would be to store both the start time and duration as intervals; then you can just add the values:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name (
start_time INTERVAL DAY TO SECOND,
duration_in_sec INTERVAL DAY TO SECOND
);
INSERT INTO table_name
SELECT INTERVAL '12:03:11' HOUR TO SECOND, INTERVAL '220' SECOND FROM DUAL UNION ALL
SELECT INTERVAL '11:05:33' HOUR TO SECOND, INTERVAL '345' SECOND FROM DUAL UNION ALL
SELECT INTERVAL '23:59:59' HOUR TO SECOND, INTERVAL '1' SECOND FROM DUAL;
Query 1:
SELECT t.*,
start_time + Duration_in_sec AS end_time
FROM table_name t
Results:
| START_TIME | DURATION_IN_SEC | END_TIME |
|--------------|-----------------|--------------|
| 0 12:3:11.0 | 0 0:3:40.0 | 0 12:6:51.0 |
| 0 11:5:33.0 | 0 0:5:45.0 | 0 11:11:18.0 |
| 0 23:59:59.0 | 0 0:0:1.0 | 1 0:0:0.0 |
Here's one way, using the numToDSInterval function:
select
to_date(start_time,'HH24:MI:SS')+numToDSInterval(Duration_in_sec, 'second')
from time_seconds;
(Uses a table named "time_seconds")
To return just the time string:
select
to_char(
to_date(start_time,'HH24:MI:SS')+numToDSInterval(Duration_in_sec, 'second'), 'HH24:MI:SS'
)
from time_seconds;
So first, it converts the timestamp from a VARCHAR to a DATE, then it increments that DATE by your number of seconds using numToDSInterval.
I would add, though, that storing a time as a string seems like a bad idea. And are your times storied in 24-hour format? Or 12-hour with AM/PM? How can you be sure that your strings are parseable into Dates?
Etc.? You should be using the right datatypes for the right job.