Oracle Timestamp Conversion with Dates - sql

Assuming this has a simple solution, but I can't find it.
I'm trying to do some logic on a DATE field in Oracle. My desire is to take a DATE field and subtract X hours from it.
For instance: SELECT A.MyDATE - 100 Hours from dual;
however, I need a result in a timestamp format 'YYYY-MM-DD hh:mm'.
I've tried CAST(A.MyDATE as TIMESTAMP) - NUMTODSINTERVAL(100/24,'day') however it didn't work.
I found out that the issue is that the MyDATE field when cast to a timestamp still contained some residual time elements. How can I reset these??
Thanks!

You can just do this with subtraction:
select a.MyDate - 100.0/24
To convert to varchar:
select to_char(a.MyDate - 100.0/24, 'YYYY-MM-DD')
And, if you want to get rid of that pesky time on the date:
select trunc(a.MyDate - 100.0/24) as JustTheDate

The formats and dates in my example can be changed to any other formats and dates:
SELECT To_Timestamp(To_Char(Sysdate - INTERVAL '100' HOUR, 'MM/DD/YYYY HH24:MI'), 'MM/DD/YYYY HH24:MI')
FROM dual
/
Output:
2/4/2013 10:18:00.000000000 AM
To remove time element add Trunc() to any of your dates...:
SELECT Trunc(To_Timestamp(To_Char(Sysdate - INTERVAL '100' HOUR, 'MM/DD/YYYY HH24:MI'), 'MM/DD/YYYY HH24:MI'))
FROM dual
/
Output: 2/4/2013
Conversion/Casting - when using other dates in place of sysdate then add formats as in my other examples:
SELECT CAST(SYSDATE AS TIMESTAMP) - INTERVAL '100' HOUR FROM dual
/
Output: 2/4/2013 10:26:35.000000000 AM
SELECT start_date tstamp_to_date, CAST(start_date AS timestamp) date_to_tstamp FROM
(
SELECT to_date(to_char(to_timestamp ('2013-02-07 10:07:47.000' , 'YYYY-MM-DD HH24:MI:SS.FF'),'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS') start_date
FROM dual
)
/
Output:
tstamp_to_date date_to_tstamp
-------------------------------------------------------
2/7/2013 10:07:47 AM 2/7/2013 10:07:47.000000 AM

In Oracle, a DATE always has a day and a time component. Depending on the tool you are using and your session's NLS_DATE_FORMAT, it is entirely possible that the tool may not display the time component when you look at the data. But that is simply a display question, it has no impact on the actual data.
If you want to subtract 100 hours from midnight on the day that MyDate represents
SELECT TRUNC(MyDate) - interval '100' hour
FROM dual
This will return a DATE. If you want to return a string in a particular format
SELECT TO_CHAR( TRUNC(MyDate) - interval '100' hour, 'YYYY-MM-DD hh:mi am' )
FROM dual
Note that I'm assuming that there was a typo in your question. I assume that you want to display the minutes after the hour (mi) rather than the month (mm).

I am trying to fetch the records which is older than 30 days (from Mod_date) and I am using the below query and it is returning all the data and I want only 30 days old data.
Sample :- Mod_date 03-NOV-12 12.00.00.000000000 AM
Query :-
select Mod_date from fil_cnfact where Mod_date <= sysdate -30 order by Mod_date asc ;

Related

Column based on time range in Oracle

I have a sales table with created datetime, my business hours are from 9 AM to 2 AM in the night on the following day. I am trying to convert the dates into my business date.
01/08/22 09:39:12.000000000 AM +04:00
Lets say I have a sale at 1 AM, this sale has to be considered in the previous day.
Any function that can help me solve this issue would be appreciated
It might be a bit of an overkill, but you could just use EXTRACT:
WITH dat AS
(
SELECT to_date('01/08/22 09:39:12','DD/MM/YY HH24:MI:SS') AS t_stmp FROM dual UNION ALL
SELECT to_date('02/08/22 01:03:15','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 08:27:33','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 14:11:51','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 02:01:15','DD/MM/YY HH24:MI:SS') FROM dual
)
SELECT CASE WHEN EXTRACT(HOUR FROM CAST(t_stmp AS TIMESTAMP)) BETWEEN 2 AND 8 THEN -1
ELSE 0
END + TRUNC(t_stmp,'DD') AS business_date
FROM dat;
business_date
01.08.2022
02.08.2022
01.08.2022
02.08.2022
01.08.2022
It looks like you just need to make a 2 hour shift to get your sales in the right date. You can add or substract hours from DATE/DATETIME/TIMESTAMP data type. If your column is TIMESTAMP then it would be like this:
-- when selecting data for date of sales
SELECT TRUNC(your_column_name - INTERVAL '2' HOUR, 'dd') "SALE_DATE"
-- And/Or
WHERE TRUNC(your_column_name - INTERVAL '2' HOUR, 'dd') = :DATE_OF_SALES
-- TRUNC function always returns DATE datatype
--
-- The opposite conversion would be
CAST(your_datetime_column + INTERVAL '2' HOUR as TIMESTAMP) ...
Here is the small sample with result:
SELECT
to_char(SYSDATE, 'dd.mm.yyyy hh24:mi:ss') "DATETIME",
to_char(SYSDATE - INTERVAL '2' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_MINUS_2H",
to_char(SYSDATE + INTERVAL '2' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_PLUS_2H",
to_char(SYSDATE - INTERVAL '10' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_MINUS_10H"
FROM
DUAL
--
-- R e s u l t
--
-- DATETIME DATETIME_MINUS_2H DATETIME_PLUS_2H DATETIME_MINUS_10H
-- ------------------- ------------------- ------------------- -------------------
-- 07.08.2022 09:58:38 07.08.2022 07:58:38 07.08.2022 11:58:38 06.08.2022 23:58:38
The last column now has the date from day before.

Oracle SQL time difference in HH:MM:SS

I am trying to get time difference in Oracle database. The time difference for all the rows is okay except for one row. Here is the query
SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls
The time difference occurs with the following row:
call_end: '2020-02-20 13:00:20'
call_start: '2020-02-20 12:56:03'
The returned result is '11:55:43' which is wrong. As the correct answer should be '00:04:17'
It looks like your table already stores the call start/end times as dates, and you're doing an implicit conversion from date to string, then converting back to a date. You can see that result if you have HH instead of HH24 in your NLS_DATE_FORMAT setting:
alter session set nls_date_format = 'YYYY-MM-DD HH:MI:SS';
with calls (call_end, call_start) as (
select cast(timestamp '2020-02-20 13:00:20' as date), cast(timestamp '2020-02-20 12:56:03' as date) from dual
)
SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;
CALL_TIME
---------------------------
11:55:43
When you do
TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS')
because it's already a date you're really doing:
TO_DATE(TO_CHAR(call_end, <NLS_DATE_FORMAT>), 'YYYY/MM/DD HH24:MI:SS')
so with my setting (and yours must be similar, maybe with slashes instead of dashes) that is:
TO_DATE(TO_CHAR(call_end, 'YYYY-MM-DD HH:MI:SS'), 'YYYY/MM/DD HH24:MI:SS')
and the mismatch between HH and HH24 becomes more obvious. So, you're actually converting the strings 2020-02-20 01:00:20 and 2020-02-20 12:56:03 back to dates, and the time difference between 01:00:20 and 12:56:03 is 11:55:43. Well - actually, it is minus 11 hours:
SELECT CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP)
FROM calls;
CAST(TO_DATE(CALL_E
-------------------
-00 11:55:43.000000
but your regex isn't picking that up.
As they are dates skip that part of the conversion completely, and just cast directly to timestamps if you want intervals to work from:
SELECT MAX(REGEXP_SUBSTR (CAST(call_end AS TIMESTAMP) - CAST(call_start AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;
CALL_TIME
---------------------------
00:04:17
or use a substring instead of a regex, as #MTO showed.
You could also leave them as dates, get the difference as a fraction of a day, add that back to any nominal date at midnight, and then convert the resulting date to a string:
SELECT TO_CHAR(date '2000-01-01' + MAX(call_end - call_start), 'HH24:MI:SS') AS call_time
FROM calls;
CALL_TIM
--------
00:04:17
This won't work properly for a call that lasts more than 24 hours (used to see that a lot with modem calls, but can still happen); but neither will your interval approach. Both ignore any full days and just show the remainder. There are ways to handle that of course, but you'd need to decide how you want to display it - with separate day count (like interval does anyway), or with the 'hours' number allowed to go above 24... but then you may go above 99 hours...
Your question shows the call end time as 2020-02-20 13:00:20, which suggests that's how your client is displaying it when you query the table. Some clients (I think PL/SQL Developer, but not sure, it's been a while) use their own preferences/settings instead of honouring the session's NLS settings. But that has no effect on how Oracle behaves internally when it has to do implicit conversions.
I'm not sure if you want the result as an interval or timestamp, but this should do what you want:
select t.*, call_end - call_start,
substr(to_char(call_end - call_start, 'HH24:MI:SS'), 12, 8) as str from (select timestamp '2020-02-20 13:00:20.000' as call_end,
timestamp '2020-02-20 12:56:03.000' as call_start
from dual) t
Here is a db<>fiddle.
Assuming your data types are strings (you really should store them as DATE data types), you can use:
SELECT SUBSTR(
MAX(
( TO_DATE( call_end, 'YYYY-MM-DD HH24:MI:SS' )
- TO_DATE( call_start, 'YYYY-MM-DD HH24:MI:SS' )
) DAY(1) TO SECOND
),
4,
8
) AS call_time
FROM calls
If they are already DATE data types then, in the MAX aggregation, just use:
( call_end - call_start ) DAY(1) TO SECOND
So for your data:
CREATE TABLE calls ( call_end, call_start ) AS
SELECT '2020-02-20 13:00:20', '2020-02-20 12:56:03' FROM DUAL
This outputs:
| CALL_TIME |
| :-------- |
| 00:04:17 |
db<>fiddle here
Mr Gyl, You can as well do: db <> fiddle :This solution will also consider when the call has lapsed over days by adding those hours together:
WITH da AS (
SELECT
NUMTODSINTERVAL(TO_DATE('2020-02-20 13:00:20', 'yyyy-mm-dd hh24:mi:ss') - TO_DATE('2020-02-20 12:56:03', 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
dual ) SELECT
EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) DIFFERENCE
FROM
da
DIFFERENCE|
----------|
0:4:17 |
So you can implement as
WITH da AS (
SELECT
NUMTODSINTERVAL(TO_DATE(call_end, 'yyyy-mm-dd hh24:mi:ss') - TO_DATE(call_start, 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
calls
) SELECT
EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) TIMESTAMP
FROM
da
Please see below response but this will only work within 24hrs and returns both '00'
WITH sample_lt AS(
SELECT '2020-02-20 12:56:03' START_TIME, '2020-02-20 13:00:20' END_TIME FROM dual
)
SELECT start_time,
end_time,
TO_CHAR (TRUNC (SYSDATE) + (to_date(end_time, 'yyyy-mm-dd HH24:MI:SS') -
to_date(start_time, 'yyyy-mm-dd HH24:MI:SS')
) , 'hh24:mi:ss' ) duration
FROM sample_lt ;

Oracle query on time (and not date)

How can you query on just the time portion of an Orace date field. Ie:
select * from mytable
where
date_column < '05:30:00'
Want the query to return any rows where the time represented by date_column is less than 5:30 regardless of the date.
You can try like this:
select * from mytable
where
to_char( date_column, 'HH24:MI:SS' ) < '05:30:00'
You can see how far the date is from midnight, and filter on that:
select * from mytable
where date_column - trunc(date_column) < 5.5/24
The date_column - trunc(date_column) calculation will give you a fraction of a day, as is normal for date arithmetic. The 5.5/24 is the fraction of the day represented by the time at 05:30; 5.5 hours out of 24 hours.
If the column was a timestamp instead of a date you'd see an interval data type as the result of the subtraction. You can use an interval literal anyway if you prefer or find it easier to understand than 5.5/24 (or have more complicated times to compare, which are harder to express as a fraction):
select * from mytable
where date_column < trunc(date_column) + interval '0 05:30:00' day to second;
This way round you're comparing the date in your column with the truncated date (i.e. midnight on that day) with 5 hours 30 minutes added to it, which is 05:30 the same day.
Quick demo with simple data in a CTE, and a third very slight variant, but they all get the same result:
with mytable (date_column) as (
select to_date('2016-04-15 05:29:29', 'YYYY-MM-DD HH24:MI:SS') from dual
union all select to_date('2016-04-14 05:29:29', 'YYYY-MM-DD HH24:MI:SS') from dual
union all select to_date('2016-04-15 05:30:30', 'YYYY-MM-DD HH24:MI:SS') from dual
)
select * from mytable
where date_column < trunc(date_column) + 5.5/24;
DATE_COLUMN
-------------------
2016-04-15 05:29:29
2016-04-14 05:29:29
Note though that any manipulation of the column like this will prevent an index being used. If you have to do this regularly it might be worth adding a virtual column/index which does that calculation.
You need to cast the time back to date, otherwise you're simply doing string comparison.
Oracle doesn't really have a clever way of doing this. Simplest to cast both dates to the same day and do the comparison.
I.e.
select *
from mytable
where to_date ('01.01.2000 ' || to_char (date_column, 'hh24:mi:ss'), 'dd.mm.yyyy hh24:mi:ss') <
to_date ('01.01.2000' || '05:30:00', 'dd.mm.yyyy hh24:mi:ss')

Oracle query time on date fields

I have a column of type DATE stored data are contains date and time . I can see value when i do
select CAST(MSG_DT AS TIMESTAMP) from table;
this is the output
17-MAR-08 15:38:59,000000000
I have to select the row using
Only date
select CAST(MSG_DT AS TIMESTAMP) from
MWRB_RECEIVE where
MSG_DT >= TO_DATE( '2000-02-03' ,'YYYY-MM-DD')
and
MSG_DT <= TO_DATE( '2010-02-03' ,'YYYY-MM-DD')
Only time (eg: every message between 12:00:11 and 23:02:55)
In DB2 i can do
SELECT *
FROM TABLE
WHERE DATE(INS_TMS) = '2014-02-18'
SELECT *
FROM TABLE
WHERE TIME(INS_TMS) > '09.55.00'
In ORACLE I can't see the equivalent.
Try this:
SELECT *
FROM your_table
WHERE TO_CHAR (start_date, 'yyyy-mm-dd') = '2014-10-06'
AND TO_CHAR (start_date, 'hh24:mi:ss') > '10:00:00'
Why are you casting the column value to a TIMESTAMP when the column in the database is a DATE type? The fractional part of the seconds will always be 0, as DATE only has resolution to the seconds value. You need to add the hours,minutes, and seconds format specifier to the query:
select MSG_DT from
MWRB_RECEIVE
where MSG_DT between TO_DATE( '2000-02-03 12:00:11' ,'YYYY-MM-DD HH24:MI:SS') AND
TO_DATE( '2010-02-03 23:02:55' ,'YYYY-MM-DD HH24:MI:SS')
There is no need to split date and hour, you can have it in a single where clause
where field > to_date('20121212 12:12:12, 'YYYYMMDD HH24:MI:SS')
Check for your reference oracle to_date() as it seems the only thing you need

Insert TIME into Oracle SQL, DATE field

Is there anyway to insert only time (no date) into a SQL field of type 'Date'
I have tried inserting:
13:00
and
01:00
and
13:00pm
and
01:00pm
But keep getting the error:
Not A Valid Month
The problem is the literal. For date/time values, Oracle expects values in the form ddMMMyyyy. (Or, you can use the expression DATE '2001-01-01. Literal values are explained here.) There doesn't appear to be a default format for time without a date.
In other words, you can get the same error with cast('1/1/2001' as date).
Instead, use the to_date() function:
select to_date('10:00', 'hh:mi')
from dual
This gives you much more flexibility with formats.
Also, as Ben notes in the comments, this gives you the current date with the time specified.
Example of using EXTRACT and time only portion of a SYSDATE and some date...:
SELECT hh||':'||mi||':'||ss time_only_portion
FROM
(
Select
EXTRACT(hour From Cast(SYSDATE as timestamp)) hh,
EXTRACT(minute From Cast(SYSDATE as timestamp)) mi,
EXTRACT(second From Cast(SYSDATE as timestamp)) ss
From dual
)
/
SELECT EXTRACT(HOUR From t_stamp) hh
FROM
(
SELECT CAST(to_date('21/02/2012 06:10:00 am', 'dd/mm/yyyy hh:mi:ss am') AS TIMESTAMP) t_stamp
FROM dual
)
/