I am currently working in a project on a Oracle database. I have observed in the application code that dates are almost never used directly. Instead, they are always used in conjunction with the trunc function (TRUNC(SYSDATE), TRUNC(event_date), etc.)
Can anyone explain the reason behind using the trunc function instead of using the date directly?
A DATE in Oracle has not only a date part, but also a time part. This can lead to surprising results when querying data, e.g. the query
with v_data(pk, dt) as (
select 1, to_date('2014-06-25 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual union all
select 2, to_date('2014-06-26 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual union all
select 3, to_date('2014-06-27 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual)
select * from v_data where dt = date '2014-06-25'
will return no rows, since you're comparing to 2014-06-25 at midnight.
The usual workaround for this is to use TRUNC() to get rid of the time part:
with v_data(pk, dt) as (
select 1, to_date('2014-06-25 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual union all
select 2, to_date('2014-06-26 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual union all
select 3, to_date('2014-06-27 09:00:00', 'YYYY-MM-DD hh24:mi:ss') from dual)
select * from v_data where trunc(dt) = date '2014-06-25'
Other, somewhat less frequently used approaches for this problem include:
convert both dates with to_char('YYYY-MM-DD') and check for equality
use a between clause: WHERE dt between date '2014-06-25' and date '2014-06-26'
You use the trunc() function to remove the time component of the date. By default, the date data type in Oracle stores both dates and times.
The trunc() function also takes a format argument, so you can remove other components of the dates, not just the time. For instance, you can trunc to the nearest hour. However, without the format, the purpose is to remove the time component.
If you the column in your table, for example event_date, is indexed, then avoid using trunc on the column because if you do that then Oracle can't use the index (otherwise, you can create a function based index)
so do not do:
select *
from mytable
where trunc(event_date) < date '2014-01-01'
but instead do
select *
from mytable
where event_date < date '2014-01-02'
In the second case, Oracle can do a range scan on the index on event_date, in the first case it has to do a full table scan.
Related
help needed. I have a set of data from oracle import to tableau for calculation. But in order to do that, i need to duplicate charts as shown in table below. For example, if there is date diff between start and end, then i need to duplicate it and assign with code 0,1 depend on how many date differences. The purpose is i need to use this function in Tableau for time interval calculation. Thanks
Pregenerate codes up to max possible value and join original table to code series so that number of row duplications is determined by difference between dates on particular row:
with t (s,e) as (
select timestamp '2020-08-16 18:30:00', timestamp '2020-08-16 20:00:00' from dual union all
select timestamp '2020-08-17 08:00:00', timestamp '2020-08-18 08:00:00' from dual union all
select timestamp '2020-08-19 08:00:00', timestamp '2020-08-19 00:00:00' from dual union all
select timestamp '2020-08-20 10:00:00', timestamp '2020-08-22 03:00:00' from dual
), series (code) as (
select level - 1 from dual connect by level <= (select count(*) from t)
)
select t.*, series.code
from t
join series on trunc(e) - trunc(s) >= series.code
order by s,code;
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')
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
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 ;
I have a column named StartDate containing a date in this format: 03-03-2012 15:22
What I need is to convert it to date. It should be looking like this: DD/MM/YYYY
What I have tried without success is:
select
p1.PA_VALUE as StartDate,
p2.PA_VALUE as EndDate
from WP_Work p
LEFT JOIN PARAMETER p1 on p1.WP_ID=p.WP_ID AND p1.NAME = 'StartDate'
LEFT JOIN PARAMETER p2 on p2.WP_ID=p.WP_ID AND p2.NAME = 'Date_To'
WHERE p.TYPE = 'EventManagement2'
AND TO_DATE(p1.PA_VALUE, 'DD/MM/YYYY') >= TO_DATE('25/10/2012', 'DD/MM/YYYY')
AND TO_DATE(p2.PA_VALUE, 'DD/MM/YYYY') <= TO_DATE('26/10/2012', 'DD/MM/YYYY')
Is there a way to do this?
EDIT1: the PA_VALUE column is: VARCHAR2
You can use TRUNC on DateTime to remove Time part of the DateTime. So your where clause can be:
AND TRUNC(p1.PA_VALUE) >= TO_DATE('25/10/2012', 'DD/MM/YYYY')
The TRUNCATE (datetime) function returns date with the time portion of
the day truncated to the unit specified by the format model.
When you convert your string to a date you need to match the date mask to the format in the string. This includes a time element, which you need to remove with truncation:
select
p1.PA_VALUE as StartDate,
p2.PA_VALUE as EndDate
from WP_Work p
LEFT JOIN PARAMETER p1 on p1.WP_ID=p.WP_ID AND p1.NAME = 'StartDate'
LEFT JOIN PARAMETER p2 on p2.WP_ID=p.WP_ID AND p2.NAME = 'Date_To'
WHERE p.TYPE = 'EventManagement2'
AND trunc(TO_DATE(p1.PA_VALUE, 'DD-MM-YYYY HH24:MI')) >= TO_DATE('25/10/2012', 'DD/MM/YYYY')
AND trunc(TO_DATE(p2.PA_VALUE, 'DD-MM-YYYY HH24:MI')) <= TO_DATE('26/10/2012', 'DD/MM/YYYY')
Outside the scope of the question, but storing dates as strings is bad practice, and storing date times is even worse.
We need to convert the strings to dates in order to do any form of date processing (arithmetic, interval assessment, etc) on them
Strings offer no guarantees regarding format, so we run the risk of date corruption crashing our code. We can defend against this by employing VALIDATE_CONVERSION() (available since 12c, find out more ) but it's still a PITN
Using non-standard datatypes makes it harder to reason about the data model and the code we build over it.
We can use TRUNC function in Oracle DB. Here is an example.
SELECT TRUNC(TO_DATE('01 Jan 2018 08:00:00','DD-MON-YYYY HH24:MI:SS')) FROM DUAL
Output:
1/1/2018
Try
SELECT to_char(p1.PA_VALUE,'DD/MM/YYYY') as StartDate,
to_char(p2.PA_VALUE,'DD/MM/YYYY') as EndDate
...
If your column with DATE datatype has value like below : -
value in column : 10-NOV-2005 06:31:00
Then, You can Use TRUNC function in select query to convert your date-time value to only date like - DD/MM/YYYY or DD-MON-YYYY
select TRUNC(column_1) from table1;
result : 10-NOV-2005
You will see above result - Provided that NLS_DATE_FORMAT is set as like below :-
Alter session NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
there is also extended usage like this:
WITH dates AS (
SELECT date'2015-01-01' d FROM dual union
SELECT date'2015-01-10' d FROM dual union
SELECT date'2015-02-01' d FROM dual union
SELECT timestamp'2015-03-03 23:45:00' d FROM dual union
SELECT timestamp'2015-04-11 12:34:56' d FROM dual
)
SELECT d "Original Date",
trunc(d) "Nearest Day, Time Removed",
trunc(d, 'ww') "Nearest Week",
trunc(d, 'iw') "Start of Week",
trunc(d, 'mm') "Start of Month",
trunc(d, 'year') "Start of Year"
FROM dates;
Oracle Offical Help Page