ORA-01847 - SELECT returns no result - sql

I have a table TIGER in a schema OFO.
I have a column AS_OF_DATE with a data type DATE.
I have values in this column as follows:
2017-01-31 00:00:00
2017-02-28 00:00:00
2017-03-31 00:00:00
But I also have 1 unwanted value (that I want to delete, but I want to SELECT it via sql statement first):
0030-09-20 17:00:00
I use TOAD to see these values in Schema Browser.
I can also use a select to return me the values, this one works, it returns me results:
SELECT AS_OF_DATE from OFO.TIGER where AS_OF_DATE='2017-01-31'
But the following select does not work:
SELECT AS_OF_DATE from OFO.TIGER where AS_OF_DATE='0030-09-20'
It gives me an error:
ORA-01847: day of month must be between 1 and last day of month tips
The date is obviously in wrong format, but somehow someone managed to add that value, now I want to delete it (all the rows that have AS_OF_DATE='0030-09-20').

Your queries are relying on the evil implicit data type conversion. '2017-01-31' is a string constant not a DATE constant. The rules for this conversion are defined by the NLS settings of the client.
You have two ways of specifying a proper DATE constant in Oracle:
Use an ANSI SQL date literal
SELECT *
FROM ofo.tiger
WHERE as_of_date = DATE '2017-01-31';
or
SELECT *
FROM ofo.tiger
WHERE as_of_date = DATE '0030-09-20';
An ANSI DATE literal always specifies the DATE in the ISO format yyyy-mm-dd thus there are no implicit conversion rules regarding 1930 vs. 2030
Use Oracle's to_date() function:
SELECT *
FROM ofo.tiger
WHERE as_of_date = to_date('0030-09-20', 'YYYY-MM-DD');
By specifying the format mask the year is also un-ambigous and no implicit conversion will take place.

I would write the query as follows, TRUNC statement will rip date only from the datetime column and it would suit your need correctly :
SELECT *
from ofo.tiger
where TRUNC(as_of_date) = TRUNC(to_date('0030-09-20', 'YYYY-MM-DD'));

Instead of selecting for what it is, select what it isn't:
select AS_OF_DATE
from OFO.TIGER
where not AS_OF_DATE between '1970-01-01' and '2017-12-31'

Related

Filter observations with a string variable as date in Spark SQL

I am looking for the right syntax to use where clause to filter a string variable as date. For example, I would like to retain records with pol_expi_dt after July 01, 2020.
pol_expi_dt takes the format of '2020-12-01 00:00:00'.
What would be the right format for this Spark Sql query?
The following code returned 0 observations. So I figure I must did something wrong
SELECT *
FROM `cloud01_propertylocationmerge`.`exposure_new`
where cast(pol_expi_dt as date) >= '2020-07-01'
Since your '2020-07-01' is a string and pol_expi_dt is a timestamp, one should cast in the other format. For example when you cast the column,
date_format(pol_expi_dt, 'yyyy-MM-dd') >= '2020-07-01'
or cast both to the date format.
date(pol_expi_dt) >= date('2020-07-01')

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')

How to fetch month from date where date column is in varchar datatype. FYI using snowflake tool

How to fetch month from date where date column is in varchar datatype. FYI using snowflake tool.
For example if i want data of june month ? how can i fetch ?
You can use the TO_DATE(…) function to treat the VARCHAR column as a formatted date type, and the EXTRACT(…) function to retrieve just the month out of the date.
If your date string is formatted in a well-known manner, TO_DATE's automatic parsing (or a direct cast using the :: operator) will suffice, and you can write your query this way:
SELECT * FROM table
WHERE
EXTRACT(month, TO_DATE(varcharCol)) = 6 -- June of every year
AND EXTRACT(year, varcharCol::DATE) = 2020; -- June of 2020 alone
Alternatively, if the date is in a non-standard format, use available formatting options to make TO_DATE(…) parse it properly:
-- Dates of custom format, such as: 'June # 02 # 2020'
SELECT
EXTRACT(month, TO_DATE(varcharCol, 'MMMM # DD # YYYY')) AS month
FROM table
WHERE
month = 6;
Note: You can also swap all DATE and TO_DATE above with TIMESTAMP and TO_TIMESTAMP if the data carries a whole timestamp value within it instead of only a date.
First of all, you shouldn't store dates as strings. But you probably know that.
If you do store dates as strings, you store them all in one particular format, say, 'mm/dd/yyyy'. So, use a substring function to get the month digits.
For 'mm/dd/yyyy':
where substring(date_string, 1, 2) = '06'
For 'yyyy-mm-dd':
where substring(date_string, 9, 2) = '06'
In many situations you can also use LIKE:
For 'mm/dd/yyyy':
where date_string like '06%'
For 'yyyy-mm-dd':
where date_string like '%-06-%'
You have to use to_date in snowflake to convert varchar datatype to date as following
select *
from yourTable
where to_date(yourDateColumn, 'YYYY-MM-DD') >= '2020-06-01'
and to_date(yourDateColumn, 'YYYY-MM-DD') <= '2020-06-30'

What is Oracle's Default Date Format?

I have an Oracle DB, and I don't control the date format. I want to know what the date format is to ensure that searches like
select * from search where search_date>='03/16/2016 00:00:00'
work as expected.
Don't do that - you are relying on implicit data type conversion which is going to fail at some point.
You have two options:
1) Use a proper ANSI SQL date literal:
select *
from search
where search_date >= timestamp '2016-03-16 00:00:00';
2) use to_date() (or to_timestamp()) and use a custom format.
select *
from search
where search_date >= to_date('03/16/2016 00:00:00', 'mm/dd/yyyy hh24:mi:ss');
With to_date() you should avoid any format that is language dependent. Use numbers for the month, not abbreviations (e.g. 'Mar' or 'Apr') because they again rely on the client language.
More details can be found in the manual: https://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements003.htm#SQLRF51062
Never rely on implicit data type conversion.
You can get all the NLS session parameters with the query:
SELECT * FROM NLS_SESSION_PARAMETERS;
or, if you have the permissions GRANT SELECT ON V_$PARAMETER TO YOUR_USERNAME;, you can use the command:
SHOW PARAMETER NLS;
If you just want the date format then you can do either:
SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT';
or
SHOW PARAMETER NLS_DATE_FORMAT;
However, you could also use ANSI date (or timestamp) literals which are format agnostic. An ANSI date literal has the format DATE 'YYYY-MM-DD' and a timestamp literal has the format TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF9'. So your query would be:
select * from search where search_date>= DATE '2016-03-16'
or
select * from search where search_date>= TIMESTAMP '2016-03-16 00:00:00'
What is Oracle's Default Date Format?
A DATE doesn't have any format. Oracle does not store dates in the format you see. It stores it internally in 7 bytes with each byte storing different components of the datetime value.
Byte Description
---- -------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
To display, use TO_CHAR with proper FORMAT MODEL.
For comparing, use TO_DATE with proper FORMAT MODEL.
What you see as a format by default, is your locale specific NLS settings.
SQL> select parameter, value from v$nls_parameters where parameter='NLS_DATE_FORMAT';
PARAMETER VALUE
--------------- ----------------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
SQL> select sysdate from dual;
SYSDATE
---------
17-MAR-16
SQL> select to_char(sysdate, 'mm/dd/yyyy hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'MM
-------------------
03/17/2016 12:48:41
SQL>
search_date>='03/16/2016 00:00:00'
You are comparing a DATE with a string literal. Always, explicitly convert the string into date using TO_DATE and proper format mask.
TO_DATE('03/16/2016', 'MM/DD/YYYY')
Or, if you dealing only with the date part and not concerned with the time portion, then use the ANSI date literal which uses a fixed format DATE 'YYYY-MM-DD'
DATE '2016-03-16'
You might just be lucky to get an output due to an implicit datatype conversion based on your locale specific NLS settings. Never ever rely on implicit datatype conversion, it might work for you, might fail for others where the nls settings are different.

Retrieve data within a date range in Oracle

I have a table tbldeptdivision as follows:
ID DEPTID DIVISIONID FROMDATE TODATE REMARKS
--- ------- ----------- ----------- ----------- --------
21 21 5 31-AUG-99 01-JAN-80 NULL
I have the query
select *
from tbldeptdivision
where deptid = 21
and trunc(sysdate) between to_date(fromdate,'dd-Mon-yyyy')
and to_date(todate,'dd-mon-yyyy');
It returns me no value. Can anybody say why? '31-AUG-99' is actually '31-AUG-1999' and '01-JAN-80' is actually '01-JAN-2080'. What will be the exact query?
Assume FROMDATE/TODATE datatype is varchar2 then when you do to_date;
select to_date('01-JAN-80','dd-mon-yyyy') from dual;
OutPut: January, 01 0080 00:00:00
So it wont be '01-JAN-2080' but '01-JAN-0080'
Even if FROMDATE/TODATE datatype is date tusing to_date is not a good idea;
create table t(c date);
insert into t select sysdate from dual;
select c, to_date(c,'dd-mon-yyyy') from t;
OutPut:
C TO_DATE(C,'DD-MON-YYYY')
August, 25 2015 10:55:36 August, 25 0015 00:00:00
Still the year is 0015 not 2015.
If your columns datatype is date then use trunc to get thedate portiondon't useto_date`.
select *
from tbldeptdivision
where deptid=21
and trunc(sysdate) between trunc(fromdate)
and trunc(todate)
As your todate is a date your problem stems from the useless conversion of the column's value from a date to a varchar and back to a date:
to_date() converts a VARCHAR to a DATE value. If the value you pass to that function is already a DATE Oracle will first implicitely convert your date to a varchar by applying the default NLS format and will then convert that varchar back to a date, again applying the default NLS format.
In the first (implicit) conversion you are losing the century in your year, which consequently is then wrong when the varchar is converted back to a date
So in your case the following is done due to the call to_date(fromdate,'dd-Mon-yyyy')
todate contains the (real) date value: 1980-01-30
the implicit conversion to a varchar makes that '01-JAN-80'
the conversion from the varchar to a date then assumes the year 80 should be 2080 (again based on the rules for implicit data type conversion).
The general rule is:
Do NOT use to_date() on a DATE (or TIMESTAMP) column
If you need to get rid of the time part in the DATE column use trunc() instead:
where trunc(sysdate) between trunc(fromdate) and trunc(todate)
Using functions on fields in your where clause slows down production. This is the same logic and will run faster.
where fromdate <= trunc(sysdate)
and todate > trunc(sysdate )