literal does not match format string-version 11 - sql

In my SQL query I have,
TO_DATE('2019-07-22' , 'YYYY-MM-DD')
I want to pass sysdate-1 instead of hard coding the date value.
I tried this,
select TO_CHAR(to_date(
sysdate-1,'DD-Mon-YY'),'YYYY-MM-DD')dates from dual
and replaced the same in the SQL query but am getting the below error,
ORA-01861: literal does not match format string
01861. 00000 - "literal does not match format string"
*Cause: Literals in the input must be the same length as literals in
the format string (with the exception of leading whitespace). If the
"FX" modifier has been toggled on, the literal must match exactly,
with no extra whitespace.
*Action: Correct the format string to match the literal.

Sysdate is a function that returns DATE datatype; you don't TO_DATE it. Therefore:
SQL> select to_char(sysdate - 1, 'yyyy-mm-dd') result from dual;
RESULT
----------
2019-08-01
SQL>
If you meant to use date datatype, then - as comments suggest - there's no need for any TO_something function:
SQL> select sysdate as today,
2 sysdate - 1 as yesterday
3 from dual;
TODAY YESTERDA
-------- --------
02.08.19 01.08.19
SQL>
The result of such a subtraction is date datatype. The way it is presented to you depends on NLS settings; in my database, it is set to dd.mm.yy. In order to present it differently, you'd either modify settings or apply to_char with appropriate format mask, e.g.
SQL> alter session set nls_date_format = 'yyyy-mm-dd';
Session altered.
SQL> select sysdate as today,
2 sysdate - 1 as yesterday
3 from dual;
TODAY YESTERDAY
---------- ----------
2019-08-02 2019-08-01
SQL>

Although sysdate is a date data type, Oracle still stores a time component.
Your code (attempts to) remove the time component. For this, use trunc():
select trunc(sydate - 1)
or
select trunc(sysdate - interval '1' day)
Note that the time component is often now shown when you select the data and look at it.

Related

Oracle regexp to validate time

I am trying to validate a regular expression in oracle SQL to check Time format:
The desired format I want to check is:
2/27/2020 3:53:02 PM
I have already created a regexp for a date format such as:
20200227 --> using REGEXP_LIKE('20190222', '^\d{4}(0[1-9]|(1[0-2]))(0[1-9]|[1-2][0-9]|3[0-1])$')
Could someone give me a hint for the time format?
To me, it looks as if you started it wrong. If you want to validate date (time) format, you're storing it as a string, which is a big mistake.
If you set that column (or whatever it is) as DATE, which - in Oracle - contains both date and time, then database will take care that you can enter only valid values.
Format you mentioned, or any other, is matter of display, not storage.
One option is to force TO_DATE conversion; something like this:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> set ver off
SQL> select to_date('&date_value', 'mm/dd/yyyy hh:mi:ss am') result from dual;
Enter value for date_value: 2/27/2020 3:53:02 pm
RESULT
-------------------
27.02.2020 15:53:02
SQL> /
Enter value for date_value: 13/54/2020 x:23:83 am
select to_date('13/54/2020 x:23:83 am', 'mm/dd/yyyy hh:mi:ss am') result from dual
*
ERROR at line 1:
ORA-01843: not a valid month
SQL>
So: if conversion works, then format is OK. Otherwise, you entered something stupid and conversion won't pass. Such a code can be used as a function which returns e.g. Boolean.
Don't use a regular expression as the edge cases will make the expression long and complicated (i.e. months can have 28-31 days, leap years every 4 years ... except for multiples of 100 ... except for multiples of 400).
For example, your date regular expression ^\d{4}(0[1-9]|(1[0-2]))(0[1-9]|[1-2][0-9]|3[0-1])$ would validate 20190229 or 20200230 or 20200931 neither of which are valid dates.
A time regular expression is easier as you only have hours 0-23, minutes 0-59, seconds 0-59 (assuming you are ignoring leap seconds) and possibly fractional seconds and would be:
([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?
However, a simpler way is just to try to perform a conversion to a DATE and if it fails then you know it isn't valid. If you are on Oracle 12.2 or later then you can use the built-in VALIDATE_CONVERSION function:
SELECT validate_conversion( '20200230000000' AS DATE, 'YYYYMMDDHH24MISS' )
FROM DUAL
If you are on an earlier version then you can create a custom function to try and perform the conversion and if an exception occurs then you know the input is invalid:
CREATE FUNCTION isValidDate(
date_string IN VARCHAR2,
format_model IN VARCHAR2 DEFAULT 'FXYYYYMMDDHH24MISS'
) RETURN NUMBER
IS
d DATE;
BEGIN
d := TO_DATE( date_string, format_model );
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
/
While looking into string Regex, I found that this one does the job that I'm looking for:
REGEXP_LIKE('2/27/2020 3:53:02 PM','^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{1,2}:\d{1,2} [AP]M\z')

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.

ORA-01843: not a valid month error when using to_date function

SELECT sysdate as today,
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss'))) as time
FROM DUAL
Every time I execute this query it gives me ORA-01843: not a valid month
Could someone please help me?
As I understand, you want to see time from sysdate, if so you need this select:
SELECT sysdate as today,
to_char(sysdate,'MM-DD-YYYY hh24:mi:ss'))) as time
FROM DUAL
In your example wrong part is to_date, you should use mask for to_date:
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss')),'MM-DD-YYYY hh24:mi:ss')
But this will return exactly the same as sysdate because both of this columns are date and the format of dates depends on variable NLS_DATE_FORMAT, check here
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss'))) as time
It is simply useless what you are doing. You are extracting date and time portions separately and then converting it back to DATE. It is nothing but SYSDATE itself.
It would make sense if you are extracting and displaying the date and time elements. Or, you have date and time as string literals separately, and now you want to convert it into a DATE.
For example,
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT to_date(CONCAT ('01/11/2016', '14:45:20'), 'MM/DD/YYYYHH24:MI:SS') my_date
2 FROM DUAL;
MY_DATE
-------------------
01/11/2016 14:45:20
ORA-01843: not a valid month
Reason
This mostly happens due to NLS dependency. Since you are not using an explicit format mask, Oracle is trying to implicitly convert it based on your locale-specific NLS settings.
Example
Let's change the NLS format of the session:
SQL> alter session set nls_date_format='dd/mon/yyyyhh24:mi:ss';
Session altered.
Let's execute the same query:
SQL> SELECT to_date(CONCAT ('01/11/2016', '14:45:20')) my_date
2 FROM DUAL;
SELECT to_date(CONCAT ('01/11/2016', '14:45:20')) my_date
*
ERROR at line 1:
ORA-01843: not a valid month
As expected, it throws ORA-01843: not a valid month.

Perl and Oracle DB - help

I am using Oracle express database, and I would like to know how can I change the date formatting-
from dd-mm-yyyy to dd-mm-yyyy hh-mm. Also, I've heard something about alter session, but I don't know how to use it in Perl.
This is what I did so far:
my $sth = $dbh->prepare("INSERT INTO Perl
(A_FIELD,B_FIELD,C_FIELD,TIME_STAME)
VALUES
(?,?,?,TO_DATE(?,'DD/MM/YYYY HH24:MI'))");
Date fields in Oracle are not formatted for display - it's an internal format that you convert to/from on input/output. When you store a date in Oracle date datatype columns, you convert your character string to internal format by describing the date-time to the TO_DATE function with the format model string. Oracle interprets the character string to it's internal format. When you need to display the date, you do the reverse - you tell oracle how to display the date by again giving a format model, this time to the TO_CHAR function.
To illustrate with your example, you could convert dd-mm-yyyy to dd-mm-yyyy hh-mm without ever storing the value (I assume you meant to display hours-minutes. The format model for minutes is 'MI', since 'MM' is month):
SQL> SELECT TO_CHAR(TO_DATE('01-01-2020','DD-MM-YYYY'),'DD-MM-YYYY HH-MI') mydate
FROM DUAL;
MYDATE
----------------
01-01-2020 12-00
Note that with your example, the time portion of your date is not supplied on input, so it defaults to midnight. To store a time value in your date column, you must supply a time value in your input:
SQL> SELECT TO_CHAR(TO_DATE('01/01/2020 10:13','DD/MM/YYYY HH:MI'),'DD-MM-YYYY HH-MI') mydate
FROM DUAL;
MYDATE
----------------
01-01-2020 10-13
SQL>
Depending on what you're trying to do, the system date in Oracle can be obtained by a reference to the pseudo-column SYSDATE:
SQL> SELECT TO_CHAR(sysdate,'MM/DD/YYYY HH:MI:SS AM') dt1,
2 TO_CHAR(sysdate,'DD-MON-YYYY HH24:MI:SS') dt2
3 FROM dual;
DT1 DT2
---------------------- -----------------------------
07/01/2011 03:44:30 PM 01-JUL-2011 15:44:30
SQL>
So the roundabout answer to your question is that it entirely depends on what format your input date string is in. You convert that to Oracle's date type via a format model and the TO_DATE function, then convert the date item to a display format of your choosing via TO_CHAR and a format model. As for the "ALTER SESSION" command you alluded to in your question, you can specify a default format model for date conversions by specifying the NLS_DATE_FORMAT parameter in the ALTER SESSION command:
SQL> SELECT sysdate FROM dual;
SYSDATE
---------
02-JUL-11
SQL> ALTER SESSION SET nls_date_format='dd-mon-yyyy hh24:mi:ss';
Session altered.
SQL> SELECT sysdate FROM dual;
SYSDATE
--------------------
02-jul-2011 10:39:24
If the incoming date string is in mm-yyyy format, then you can use the statement below(TO_DATE(?,'MM-YYYY')) to convert the string to date:
$sth = $dbh->prepare("INSERT INTO Perl (A_FIELD,B_FIELD,C_FIELD,TIME_STAME) VALUES (?,?,?,TO_DATE(?,'MM-YYYY'))");

Why does RPAD() on a Date column return only the date component?

I have a column in a table with a data type as DATE. When I fetch the column via a query (I've used SYSDATE as an example, but the behavior is the same), I get the date/time - which I understand.
SELECT SYSDATE
FROM DUAL
SYSDATE
--------------------
21-Feb-11 12:24:39 PM
Now, using rpad() returns only the date part
SELECT SYSDATE, RPAD(SYSDATE, '9')
FROM DUAL
SYSDATE | RPAD(SYSDATE, '9')
----------------------|-------------------
21-Feb-11 12:27:14 PM | 21-FEB-11
Oracle documentation states:
RPAD returns expr1, right-padded to length n characters with expr2, replicated as many times as necessary. If expr1 is longer than n, then this function returns the portion of expr1 that fits in n.
Now sysdate returns characters > 9, so why doesn't, say rpad(16) return the date and the time ?
SELECT SYSDATE, RPAD(SYSDATE, '16')
FROM DUAL
SYSDATE | RPAD(SYSDATE, '16')
----------------------|-------------------
21-Feb-11 12:27:14 PM | 21-FEB-11
RPAD is a string function, so when you apply it to a DATE value Oracle first has to implicitly convert the date to a string, which it does using the session's default format mask, which usually does not include the time component. Try this instead:
SELECT SYSDATE, RPAD (TO_CHAR(SYSDATE,'DD-Mon-YY HH:MI:SS'), 16)
FROM DUAL;
Having said that, you are getting the time when you just select SYSDATE. If I try to replicate your case I see this:
SQL> alter session set nls_date_format = 'DD-MON-RR HH24:MI:SS';
SQL> select sysdate, rpad(sysdate,16) from dual;
SYSDATE RPAD(SYSDATE,16)
------------------ ----------------
21-FEB-11 11:20:20 21-FEB-11 11:20:
i.e. pretty much what you were hoping to see. Which makes me wonder: how are you setting the format so that SELECT SYSDATE FROM DUAL shows the time?