How to transform Oracle DateTime to Date - sql

For instance, I have a datetime like this '2016-04-02 00:00:00' and another like this '2016-04-02 15:10:00'. I don't care about the time-part, I want them to match just by the date-part.
I have tried with date(), to_date, datepart, nothing works.

Do it like this:
where yourField >= the start of your date range
and yourField < the day after the end of your date range
Edit starts here:
While you could use trunc, as suggested by others, bear in mind that filtering on function results tends to be slow.

Truncating the date to day should do the trick. Documentation here:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions201.htm
For example
SELECT TRUNC(SYSDATE, 'DAY') FROM DUAL;

As others have said - there is no separate "date" data type in Oracle. A pure "date" is stored as a date with the time portion set to 00:00:00 (midnight at the beginning of the day), and TRUNC(date) will take any date and truncate the time to 00:00:00, so if you need to compare two dates you can write
where trunc(date_1) = trunc(date_2)
If your tables are very large and you need to do these comparisons often, this is not ideal, because wrapping column values within function calls (like date_1 within a TRUNC) prevents the use of an index you may have on the date_1 column. If you need to compare dates in two columns you may not have much of a choice, but if you compare to a fixed date (or something like SYSDATE) you may be better off with something like
where date_1 >= trunc(sysdate) and date_1 < trunc(sysdate) + 1
Here you are not using trunc on the column value, so if there's an index on the column, Oracle is free to use it - and trunc(sysdate) is computed only once, not for every single row. "+1" by the way means "add one day".
TO_DATE converts a string to a date; if you apply TO_DATE to a value that is already a legitimate date, you will get unexpected results because Oracle will first convert your true date to a string and then back to date again, and since these conversions require a date FORMAT for strings, and the formats Oracle assumes for conversion from date to string and from string to date may not match, .... you get the idea. As far as I know, DATE() (a FUNCTION) and DATEPART do not exist in Oracle; when you use a new language, keep Google close by and use it often.
If you input a date with no time component, for example TO_DATE('04-apr-2016, 'dd-mon-yyyy'), then the implicit time is 00:00:00 so you don't need to apply TRUNC() to it.
Good luck!

Related

Using the To_Date function is not converting to the expected format

I'm trying to convert a char (output from a substr operation) into a dateTime format using TO_DATE with the following format - hh24:mi:ss.
The output of the substr looks fine but as soon as I run it through the TO_DATE function it converts every row for this column into 01-MAR-22.
To demonstrate I have the following:-
SUS_TIME2 shows what I get back from the SUBSTR and it looks fine at this point. SUS_TIME3 then shows what I get back after running it through the TO_DATE function, this where it converts it to 01-MAR-22
,SUBSTR((s.resolve_date-suspend_date),-16,9) sus_time2
,TO_DATE(SUBSTR((s.resolve_date-suspend_date),-16,9), 'hh24:mi:ss') sus_time3
Can anyone see what's going on here please? Thanks
Dates are stored in an internal representation and do not have any intrinsic format. What you are seeing is how your client is choosing to format the actual date value as a string for display, using your session's NLS_DATE_FORMAT setting (not always the case, but this is SQL Developer, so it is here.)
When you call to_date() with only the time components it defaults the date part to the first day of the current month, which is why you are seeing March 1st. That is a bit buried in the documentation:
If you specify a date value without a time component, then the default time is midnight. If you specify a date value without a date, then the default date is the first day of the current month.
But it is setting the time properly on that date, which you can see by either explicitly converting the date back to a string:
to_char(<your date>, 'YYYY-MM-DD HH24:MI:SS')
or by changing your session:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
db<>fiddle
But you should not rely on NLS settings for anything except ad hoc queries; someone else running your code may have different settings. And only convert back to a string at the last moment when you have to display the value in a particular format - store it and pass it around as a native date.
Oracle doesn't have a time-only data type, so if you really only care about the time part then you can use a date (either defaulting to current month, or using an explicit fixed date) and ignore the date part; or potentially use an interval; or use the number of seconds the time represents (i.e. 0-86399). Which is suitable depends on what you'll use the value for.
It looks like you might be substringing the result of subtracting two timestamps; in which case (a) you already have an interval, and (b) you probably need to allow for that difference to span more than one day. You can also extract the individual time components directly from an interval value. So I'd question whether your approach is really appropriate.
You appear to be calculating s.resolve_date-suspend_date which gives you an INTERVAL DAY TO SECOND data type (implying that one or both of s.resolve_date or suspend_date is a TIMESTAMP data type) and then using SUBSTR to extract the time component of the data type and trying to convert that to a date and then display the time component.
Don't do that as using SUBSTR is fragile as it depends on the number of decimal places that the INTERVAL has which, in turn, will depend on the number of fractional seconds that the TIMESTAMP values have.
Just pick a date and add the interval to it and then format it as string to display it:
SELECT s.resolve_date,
suspend_date,
TO_CHAR(
DATE '1900-01-01' + (s.resolve_date-suspend_date),
'hh24:mi:ss'
) sus_time
FROM table_name s
Which, for the sample data:
CREATE TABLE table_name (resolve_date, suspend_date) AS
SELECT CAST(SYSTIMESTAMP AS TIMESTAMP(6)),
CAST(TRUNC(SYSTIMESTAMP) AS TIMESTAMP(6))
FROM DUAL;
Outputs:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 12:59:15.223445
2022-03-25 00:00:00.000000
12:59:15
From your comment:
all i'm trying to do is format the data so that when it's exported to Excel sorting works correctly.
That really is an XY-problem. You can solve it in Excel by either specifying the column format as a time when you import the data into Excel or right-click on the column header and "Format" the column picking the "time" data type with the correct format model.
You can also output the time as a fraction of a day with a numeric data type and then Excel can format it as the correct time using either:
SELECT s.resolve_date,
suspend_date,
MOD(CAST(s.resolve_date AS DATE)-CAST(suspend_date AS DATE), 1) AS sus_time
FROM table_name s;
or
SELECT s.resolve_date,
suspend_date,
TO_CHAR(DATE '1900-01-01' + (s.resolve_date-suspend_date), 'SSSSS')
/ 86400 AS sus_time
FROM table_name s
Which both output:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 13:16:40.204461
2022-03-25 00:00:00.000000
.5532407407407407407407407407407407407407
Which may not be human readable in that format but Excel will reformat it in a time column to 13:16:40.
db<>fiddle here

Rolling 12 months oracle

I am writing a query in Oracle that I want to automate. I currently have a where clause that has the following:
TRAN.DATE BETWEEN 20160101 AND 20161231
I want the query to be a 12 month look back from the current date. I think I can do it if the date was in a date format using the following:
TRAN.DATE BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
But the date format in the table is in an integer format. I have tried to the TO_DATE function but I can't get the format right.
Yikes, you mean your database table literally has an integer of 20170104 (twenty million, one hundred seventy thousand, one hundred and four) and this represents the forth of Jan, this year?
Setting aside any concerns I have about this not being the best way to store a date, it's probably going to be easiest to write and also understand, if you convert your date to char and then to int:
WHERE DATECOLUMN BETWEEN
TO_NUMBER(TO_CHAR(ADD_MONTHS(SYSDATE,-12),'YYYYMMDD')) AND
TO_NUMBER(TO_CHAR(SYSDATE,'YYYYMMDD'))
There's no point getting clever and trying to deal with it as an integer math problem by subtracting 1200 or 10000 etc as a number because it'll make your query hard to understand and have no performance gain at all over a couple of number conversions as per this way
You'd be better off changing those dates to actual dates right now, before the codebase grows any bigger and someone decides it's a good idea to add more date columns stored as ints
You could try
TO_DATE(TRAN.DATE, 'YYYYMMDD') BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
According to Oracle documentation,
TO_DATE converts char of CHAR, VARCHAR2, NCHAR, or NVARCHAR2
datatype to a value of DATE datatype
so you need to first cast integer column to char or any of the accepted datatype specified above, and then cast it to DATE as below.
SELECT TO_DATE(to_char(20160101),'YYYYMMDD') as DATE1 FROM DUAL
Result
DATE1
-------------------
01.01.2016 00:00:00
so you need to rewrite your query as below.
TO_DATE(to_char(TRAN.DATE),'YYYYMMDD') BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE
Also if you need only date part, you amy also need to trunc sysdate.
You can check the demo here
Hope this will help.

Sysdate Oracle to Run Based on Day

This is probably an easy question for most of you but how can I get this mask to run based on just the day?
If anyone knows Crystal Reports syntax, we have this and it works {PO_RECEIPTS.DATE_RECEIVED} = currentdate
However, when converting to Oracle SQL, how can I the standard: TO_CHAR
(SYSDATE, 'MM-DD-YYYY HH24:MI:SS') to become range so we can selected everything during the day, not just what matched the second in which the report was ran which it never will.
So something like Today from 00:00:00 to 23:59:59 ?
Thank you!
If PO_RECEIPTS.DATE_RECEIVED is a date column where all the times are set to midnight then you can do:
WHERE PO_RECEIPTS.DATE_RECEIVED = TRUNC(sysdate)
If the values have other times then you can use a range:
WHERE PO_RECEIPTS.DATE_RECEIVED >= TRUNC(sysdate)
AND PO_RECEIPTS.DATE_RECEIVED < TRUNC(sysdate) + 1
Truncating a date sets the time to midnight, by default, so TRUNC(sysdate) is midnight this morning. For the range you get all records equal to or later than midnight this morning, and less than midnight tomorrow - which is what TRUNC(sysdate) + 1 gives you, using normal Oracle datetime arithmetic.
You don't really want to convert it to a string with TO_CHAR(); you'd either have to convert all the column values to strings too (which is inefficient and prevents an index being used), or let the string be (implicitly) converted back to a date anyway. It's better to compare a column value with the same data type to reduce or avoid confusion.

Issue with date formatting SQL

SELECT LISTING_EOD.LOCATION, LISTING_EOD.APPTTIME, LISTING_EOD.PERSON_ID,
LISTING_EOD.FORENAME, LISTING_EOD.SURNAME, LISTING_EODS.STATUS,
LISTING_EOD.DBDATE
FROM DBNAME.LISTING_EOD LISTING_EOD;
This query returns a list of data processed today, I need to modify to check yesterday's data. I have tried add the below line of code, but it doesn't return anything. Does anyone know how I can achieve this?
where LISTING_EOD.DBDATE = '18-OCT-2012';
If you always want yesterday's data, rather than hard-coding the date you can use:
WHERE LISTING_EOD.DBDATE >= TRUNC(SYSDATE) - 1
AND LISTING_EOD.DBDATE < TRUNC(SYSDATE)
TRUNC(SYSDATE) gives you midnight this morning, so if run today it would give a range between 18-Oct-2010 00:00:00 and 18-Oct-2012 23:59:59.
It's generally not a good idea to use implicit date format masks; your original code assumes your NLS_DATE_FORMAT is set to DD-MON-YYYY, but that might not be correct now (if you're seeing the time in the existing select then it probably isn't), and may well not be in the future. Always use an explicit date format mask, like TO_DATE('18-OCT-2012', 'DD-MON-YYY'), to avoid ambiguity and unexpected behaviour.
If the field is actually VARCHAR2 rather than a DATE - which is bad - then you'll need to convert the date range to a string to get a match:
WHERE LISTING_EOD.DBDATE >= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' DAY, 'DD-MON-YYYY HH24:MI:SS')
AND LISTING_EOD.DBDATE <= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' SECOND, 'DD-MON-YYYY HH24:MI:SS')
That will work for a single day, just, but you'd have problems looking for a date range. It's much better and safer to store data in a column of the appropriate type.
Dates in Oracle by default contain time as well. If you just specify '18-OCT-2012', it will only match 18-OCT-2012 00:00:00'. One way to get around this is to format your database date to what you are comparing it to, e.g. to_char(LISTING_EOD.DBDATE, 'DD-MON-YYYY') and compare this to '18-OCT-2012'. This comparison will disregard time completely.
If you had a date variable to compare with instead of a string, format this using the same date mask used for the database date. This also gets around any assumptions abut default date format on the database in question.
I realised the 'table' I was querying was a view, examined it inside sqldeveloper, and added '-1' to the sysdate. This query then returned the previous days results.
I successfully retrieved the correct data, thanks for all help received.

how to use = assignment operator with timestamp date column in oracle

I'm using timestamp in dat column in table r3. when I fire command
select dat from r3 where dat='16-nov-09';
it shows "no rows selected" but when i fire command
select dat from r3 where dat>'15-nov-09';
it shows the whole data of 16-nov-09. Tell me what is wrong in my first command or what i have to do.
Quering on oracle date columns is always confusing. The date columntype is always a datetime. Storing the current date from sysdate stores always the time component too.
There good and evil ways quering the date columns. I show and vote some.
where to_char(DAT, 'DD-MON-YYYY') = '16-NOV-2009'
where trunc(DAT) = to_date('16-NOV-2009', 'DD-MON-YYYY')
Both bad, because they do not use any index. To avoid this, you can define a function based index on the expression.
The trick of both is to cut off the time component. If time is not needed, than it is a good advise to cut off the time in INSERT and UPDATE trigger. The function based index can convert to a normal index.
where DAT between to_date('16-NOV-2009', 'DD-MON-YYYY')
and to_date('16-NOV-2009 23:59:59', 'DD-MON-YYYY HH24:MI:SS')
where DAT >= to_date('16-NOV-2009', 'DD-MON-YYYY') and DAT < to_date('16-NOV-2009', 'DD-MON-YYYY')+1
This two are always my favorites.
Its a good advice to use to_date and to_char to convert the values between string and datetime.
As DAT is timestamp you can use as below
select DAT from R3
where DAT between to_date('16-NOV-09' , 'dd-MON-yy') and to_date('16-NOV-09 23:59:59', 'DD-MON-YY hh24:mi:ss')
Timestamp has time and date components, so query
select dat from r3 where dat='16-nov-09';
will work only for records where time component is midnight: '00:00:00'
Beside formatting (to_date function), you can truncate timestamp to get only date:
select dat from r3 where trunc(dat)='16-nov-09';
Beware that this will not use index on field dat (if there is any).
TIMESTAMP and DATE are different data types in oracle and both store time components. If you really do need to store subsecond times then you use TIMESTAMP, otherwise DATE is your best choice.
The ANSI timestamp and date literal syntaxes are quite handy:
create table ts_test (ts1 timestamp);
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00'
/
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00.1'
/
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00.001'
/
select *
from ts_test
where ts1 = date '2009-10-11'
/
use the below format for a date field in where condition.
where to_char(DAT,'mmddyyyy') = '11152009';
In Oracle the date fields also contain a time component, so 16-nov-09 is actually midnight of Nov 16th.
Two different ways to handle this:
where to_char(DAT,'mmddyyyy') = '11152009'
as john suggested, but I like the following version more:
where trunc(dat) = to_date ('11152009', 'mmddyyyy')
TRUNCfor a date "removes" the time component (or to be more specific, truncates it to midnight), and to_date is the proper way to construct a date value in Oracle SQL. (I prefer to do the comparisons in the right domain - DATEs as in the second example- over another - STRINGs as in the first example. With strings you may run into some weird month issues, sorting is easier in dates etc.)
Just to add to it , An easy way out when you are not bothered about the time-stamp but just want to compare the date is to use the 'like' operator.
for example
select dat from r3 where dat LIKE '16-nov-09%'
will give you desired output.