uisng to_date function still get date format picture ends before converting entire input string error - sql

I have the following code where I want to see if a date is less than a year ago:
select id
from mytable
where id= :p_id
and (to_date(trunc(sysdate), 'yyyy-mm-dd') - to_date(datewhen, 'yyyy-mm-dd')) < 365;
I keep getting the error:
ORA-01830: date format picture ends before converting entire input
string
Looking at other question with the same error on StackOverflow I see the solution usually is to use the to_date function which I am doing so I am unsure why this is occuring. The datewhen field is of type Date.

Do not use to_date() with the columnes of DATE data type. to_date() converts character string to a value of DATE data type. It makes no sense to convert the DATE to DATE. In a first step datewhen column of type DATE will be implicitly converted into a character data type by using the default date format (that's most probably not 'yyyy-mm-dd') and this is the culprit of the ORA-01830 error.
So your statement should look something like this:
select id from mytable where id = :p_id and (trunc(sysdate) - trunc(datewhen)) < 365;
I'd calculate the difference in the months or years instead of days:
... where months_between(sysdate, datewhen) < 12

If your datewhen column is char/varchar formatted as yyyy-mm-dd then you have to do the to_date conversion on datewhen, but not on SYSDATE: it's already a date and doesn't need to be converted.
To filter on a date within the past 365 days, compare it to SYSDATE - 365:
select id
from mytable
where id = :p_id
and to_date(datewhen, 'yyyy-mm-dd') > sysdate - 365;
But a year isn't always 365 days: on leap years it's 366 days. To get a one year ago value that's always correct, subtract an interval of one year from the current date:
select id
from mytable
where id = :p_id
and datewhen > sysdate - interval '1' year;
One more thing: the Oracle DATE type isn't just a date; it's a date and a time. SYSDATE returns the current date and time. Try this query:
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
Unless you run this at exactly midnight you'll see a time component as well.
Say your query runs on 2 September 2017 at 10 AM and you're looking for a date within the past year. You'd expect to get the date 3 September 2016, but you wouldn't because at 10 AM SYSDATE is 3 September 2016 at 10:00:00. That's greater than the plain date 3 September 2016, which is 3 September 2016 at 0:00:00, so records with a datewhen of `2016-09-03' won't be included.
To ignore the time component of an Oracle DATE value, use TRUNC. Your final query should look something like this:
select id
from mytable
where id = :p_id
and datewhen > trunc(sysdate) - interval '1' year;

you use TO_DATE function when the value in character format
Syntax
The syntax for the TO_DATE function in Oracle/PLSQL is:
TO_DATE( string1 [, format_mask] [, nls_language] )

Related

Query that will select a timeframe from specific date to today

I'm having issues in my WHERE clause selecting data from a specific day to today's date. The day/time format in my date column is '7/2/2020 3:12:08 PM'.
I've tested a couple options but keep getting this error - 'literal does not match format string'.
Any idea's of how I can select all data from March 1, 2020 to current date?
Thanks!
In Oracle date columns are not strings, they are exactly in date datatype, so you don't need to convert/cast it. Just use simple date literals:
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Literals.html#GUID-8F4B3F82-8821-4071-84D6-FBBA21C05AC1
select * from table where your_date_columg >= date'2015-12-31'
or with to_date function for your string:
select * from table
where
your_date_columg >= to_date('2019-11-25 13:57:52',
'yyyy-mm-dd hh24:mi:ss')

Oracle SQL: Syntax for WHERE clause with one date between two others

Apologies in advance, feel like I'm missing something fundamental here.
I'm limiting a query WHERE one field (already in date format) is between two others:
SELECT Stuff
FROM Table
WHERE datefield BETWEEN (currentdate - 28) AND (current_date - 1)
This returns nothing. Now if I format both as dates explicitly and look for a single date:
SELECT Stuff FROM Table
WHERE TO_DATE(datefield, 'YYYY-MM-DD') = TO_DATE((current_date - 1), 'YYYY-
MM-DD')
That returns the single day's results as intended. However, if I then try something along the lines of:
SELECT Stuff FROM Table
WHERE TO_DATE(datefield, 'YYYY-MM-DD') >= TO_DATE((current_date - 28),'YYYY-
MM-DD')
This returns the entire table, including dates from long before (current_date - 28)
And finally if I try the BETWEEN after explicitly formatting:
SELECT Stuff FROM Table
WHERE TO_DATE(datefield, 'YYYY-MM-DD') BETWEEN TO_DATE((current_date - 28)
,'YYYY-MM-DD') AND TO_DATE((current_date - 1) ,'YYYY-MM-DD')
This returns nothing again.
Any help would be much appreciated.
SELECT Stuff
FROM Table
WHERE datefield BETWEEN (currentdate - 28) AND (current_date - 1)
Will return values between the 28 days before the current date at the same time of day as now and 1 day before the current date at the same time of day as now. So, if you run this at 10:23 today then it will get results before 10:23 yesterday and if your results for yesterday were all entered in the afternoon then they will not be included in the results.
SELECT Stuff
FROM Table
WHERE TO_DATE(datefield, 'YYYY-MM-DD') = TO_DATE((current_date - 1), 'YYYY-MM-DD')
TO_DATE( date_string, format_model ) takes a string as the first argument so Oracle will implicitly convert your date to a string using the NLS_DATE_FORMAT session parameter. So your query is effectively:
SELECT Stuff
FROM Table
WHERE TO_DATE(
TO_CHAR(
datefield,
( SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT' )
),
'YYYY-MM-DD'
)
=
TO_DATE(
TO_CHAR(
(current_date - 1),
( SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT' )
),
'YYYY-MM-DD'
)
Firstly, don't ever rely on an implicit conversion. Secondly, depending on what your NLS_DATE_FORMAT session parameter is your query will either: raise an exception if the format model does not match YYYY-MM-DD; or give you gibberish dates (i.e. if your default format model is RR-MM-DD then you'll lose the centuries from your dates; if it is DD-MM-YY then you'll swap days and years - which might "work" [i.e. not raise an exception] for the next decade or so and then blow up horribly in February 2029); or it will work. However, this is a session parameter so users can change it and your query will randomly stop working without changing your SQL.
Assuming that your NLS_DATE_FORMAT is YYYY-MM-DD then this will effectively truncate your dates and you could do it much simpler using:
SELECT Stuff
FROM Table
WHERE TRUNC(datefield) = TRUNC(current_date - 1)
This will get all the results (regardless of time of day) for yesterday.
What you probably want is:
SELECT Stuff
FROM Table
WHERE datefield >= TRUNC( currentdate - 28 )
AND datefield < TRUNC( current_date )
As it will get all the results from Midnight 28 days ago to before midnight today.
If this doesn't work then you need to check your date values:
SELECT TO_CHAR( datevalue, 'YYYY-MM-DD HH24:MI:SS' )
FROM table;
You will possibly find that the dates are stored with the year in the 1st century AD (i.e. 0018-10-24 09:50:00 as the value) and this will (probably) be because dates were uploaded as a string with a 2-digit year (YY-MM-DD) when a 4-digit year (YYYY-MM-DD) was expected so Oracle will assume the century is 0.

Updating dates from 19XX to 20XX in Oracle

I have a table where dates have been incorrectly entered as 19XX rather than 20XX.
Is it possible to have an update query that will amend any value in a particular field from 19XX to 20XX (UK date format) where the original date is less than 01/01/2000?
For example
ID FieldA
123 23/11/1917
would become
ID FieldA
123 23/11/2017
Assuming that you may have correct date in the latter half of the 20th century and it is only the earlier half of the century you need to update (please confirm this) then:
UPDATE table_name
SET date_column = ADD_MONTHS( date_column, 12 * 100 )
WHERE date_column >= DATE '1900-01-01'
AND date_column < DATE '1950-01-01';
If you do want to change the dates for all years in the 20th century then:
UPDATE table_name
SET date_column = ADD_MONTHS( date_column, 12 * 100 )
WHERE date_column >= DATE '1900-01-01'
AND date_column < DATE '2000-01-01';
Note: you need to use the ADD_MONTHS function rather than adding INTERVAL '100' YEAR(3) since there are dates in the 20th century that are not in the 21st century (i.e. 1900-02-29).
You can take advantage of shortened YY date format. To quote the docs:
If you use the TO_DATE function with the YY datetime format element,
then the year returned always has the same first 2 digits as the
current year.
Therefore, to_date(to_char(fieldA, 'ddmmyy'), 'ddmmyy') should do what you need. Be careful though, if there are dates with years less than 1900 or over 2100, they'll be converted as well.
If FieldA is a date, then you can do:
select (case when fieldA < date '2000-01-01'
then fieldA + interval '100' year(3)
else fieldA
end)

Oracle SQL WHERE MONTH = N

In my select I am using this
(TRUNC(TO_DATE(TIMESTAMP, 'dd.mm.yyyyHH24:mi'))) TIMESTAMP,
to get the following output in a date format: e.g. 22/04/2016
Now I want to add a statement in my WHERE-clause to show only dates in special months, for example only dates which are in MARCH and APRIL
I tried using this:
WHERE (TRUNC(TO_DATE(TIMESTAMP, 'mm'))) in (3,4)
which gives me an error.
Thanks for helping.
Just use EXTRACT() on the timestamp:
WHERE EXTRACT(MONTH FROM timestamp) IN (3, 4)
This would match records from March and April.
In my select I am using this
(TRUNC(TO_DATE(TIMESTAMP, 'dd.mm.yyyyHH24:mi'))) TIMESTAMP,
to get the following output in a date format: e.g. 22/04/2016
TO_DATE takes a string value so your "TIMESTAMP" column will be implicitly converted to a string and then back to a date... which is unnecessary (and relies on the value of the NLS_TIMESTAMP_FORMAT session parameter to format the implicit conversion - which, if changed, will break the query); you can just do:
TRUNC( "TIMESTAMP" ) AS "TIMESTAMP"
If you want to then filter on different months then you can do (as suggested by Tim Biegeleisen):
WHERE EXTRACT( MONTH FROM "TIMESTAMP" ) IN ( 3, 4 )
or if you want a particular year then
WHERE TRUNC( "TIMESTAMP", 'MM' ) IN ( DATE '2016-03-01', DATE '2016-04-01' )
or, so you can use an index on the column:
WHERE "TIMESTAMP" >= DATE '2016-03-01'
AND "TIMESTAMP" < DATE '2016-05-01'

How to retrieve the records based on a date from oracle database

I have a table with date column in it. I need to fetch the records from it based on
the given date.
Currently when i used the query:
select * from workingemployee_data where created_date like '20-Jan-2012'
I am getting those records which have created_date on 20-Jan-2012
But i want to get the records those were created 10 days earlier to a given
date (i.e) 20-Jan-2012.
Please suggest me on this.
This gives all records between today and 10 days ago:
SELECT *
FROM workingemployee
WHERE created_date BETWEEN sysdate - INTERVAL '10' DAY
AND sysdate
This gives all records entered exactly 10 days ago:
SELECT *
FROM workingemployee
WHERE created_date = sysdate - INTERVAL '10' DAY
Replace sysdate with exact date if you want.
Why do you use like and not = ?
Assuming that created_date is of type DATE, it's bad practice to rely on implicit conversion according to NLS_DATE_FORMAT (this is what happens when you compare a date and a string)
dd-mon-yyyy isn't a good format for querying since it deffers according to NLS_LANGUAGE better use mm for months numbers
So, either use #mvp's answer or do something like this:
SELECT *
FROM workingemployee
WHERE trunc(created_date) = to_date('20-01-2013', 'dd-mm-yyyy') - 10
SELECT *
FROM workingemployee
WHERE created_date > sysdate - INTERVAL '10' DAY;