I have a varchar2 datatype field (lmp_date) that can return either null or what looks like a timestamp value. Changing the database data_type to DATE isn't a possibility, so now I'm needing to convert this to a date, but with the values this column returns, I'm having some problems.
Returned values for lmp_date =
null or 2021-06-11-00.00.00
Date format needed: MM/DD/YYYY
I've tried cast, convert, substr+instr to no avail
ETA - A couple example attempts (because there have been 10+:
select order_no, to_date(lmp_date) lmp_date from table_a - with error message of 'ORA-01861: literal does not match format string'
select order_no, to_date(substr(lmp_date, 1, instr(lmp_date, '00' -15))) lmp_date from table_a - since lmp_date has null value possibilities, this doesn't work successfully
select order_no, cast(lmp_date as date) lmp_date from table_a - with same error message of 'ORA-01861: literal does not match format string'
select order_no, to_date(lmp_date, 'YYYY-MM-DD') lmp_date from table_a - ORA-01830: date format picture ends before converting entire input string
There have been more attempts, this is all I can remember
To convert a string to a date, use the to_date() function with a suitable format mask:
to_date(lmp_date, 'YYYY-MM-DD-HH24:MI:SS')
The format model elements are in the documentation.
The result of that is a date data type, which is an internal 7-byte representation. Your client or application will format that for display, which may be based on your NLS_DATE_FORMAT setting, so you can modify that to change hot all dates are displayed; or use to_char() to convert the date back to a string, e.g.:
to_char(to_date(lmp_date, 'YYYY-MM-DD-HH24:MI:SS'), 'MM/DD/YYYY')
although if you want it as that string you can just use string manipulation with substr() and concatenation:
case when lmp_date is not null then
substr(lmp_date, 6, 2) || '/' || substr(lmp_date, 9, 2) || '/' || substr(lmp_date, 1, 4)
end
db<>fiddle
When you do either of these:
to_date(lmp_date)
cast(lmp_date as date)
this also relies on your session NLS_DATE_FORMAT; and the "literal does not match format string" error indicates that it doesn't match the string, e.g. if you have the still-default 'DD-MON-RR' setting. It would actually work - for you in your current session - if you changed that setting. I've shown that here just for info. But to work for anyone regardless of their session settings, you should use to_date() with an explicit format mask, and don't rely on or assume anything session-specific.
You were nearly there with:
to_date(lmp_date, 'YYYY-MM-DD')
and again the "date format picture ends before converting entire input string" message tells you what is wrong - your string carries on past the YYYY-MM-DD elements. Expanding the format mask to match all of the string, as I did above, means it knows what each part means.
If you were really only interested in the date part then you could cut the end off the string:
to_date(substr(lmp_date, 1, 10), 'YYYY-MM-DD')
but that's only really useful if you have a mix of string values where some have times and some do not. (The resulting date will always have a time; it will just be midnight.) And if you have dates with different formats then it gets a bit complicated - partly why you shouldn't store dates as strings.
Related
I have a column with a datatype number but I want to convert the column into date. I tried using CAST function but it gives error
ORA-00932: inconsistent datatypes: expected DATE got NUMBER.
For example, 20221203 to 2022-12-03.
Any suggestions?
col_date is the column name
select cast(col_date as date)
from school
Try converting int to varchar and then varchar to date
select cast(cast(col_date as varchar(10)) as date)
Use the to_date() function:
select to_date(col_date, 'YYYYMMDD')
from school
That does an implicit conversion from number to string, but you can make it explicit:
select to_date(to_char(col_date), 'YYYYMMDD')
from school
Of course, it would be better to store your values as proper dates. You may have numbers which don't correspond to actual dates, and will need to decide how to handle those if you do.
Oracle's date datatype always has a time component, which will be set to midnight with this conversion. They have no intrinsic human-readable format - your client decides how to display, usually using your session NLS_DATE_FORMAT setting. You can change that with alter session, which will affect the display of all date values.
If you want to display the date as a string with a particularly format then you can reverse the process with the to_char() function:
select to_char(to_date(to_char(col_date), 'YYYYMMDD'), 'YYYY-MM-DD')
from school
If you only want it reformatted as a string, and don't need it as a real date at all, you could just format the number directly:
select to_char(col_date, 'FM0000G00G00', 'nls_numeric_characters='' -''')
from school
db<>fiddle
But either way, only do that for final display - leave it as an actual date (not string) for any processing, joins, storage etc.
May I know could I verify whether the date displayed in the field is in specific format
As per my requirement, the datetime should be displayed in format 'yyyy/mm/dd HH(24hr)/MM/SS'
Eg: The valid value should be '2014/07/18 14:16:48'. If the date is displayed as '18/07/2014 14:16:48', then it is invalid.
Using query how I verify whether it is shown in the format which I have expected. I could use IsDate option to verify it is a valid date and also I could use Mid function to verify the date separator which is '/', but how could I verify the format.
Thanks
If the column is stored as Text, use the SQL Like operator. Select valid dates:
SELECT * FROM myTable WHERE myDate Like "####/##/## ##:##:##"
Select invalid dates
SELECT * FROM myTable WHERE myDate Not Like "####/##/## ##:##:##"
# stands for a single digit.
See Like Operator.
But note that this only makes sense if myDate is a Text! If the type of myDate is Date/Time, the date is stored in a numeric format internally and is only formatted as a date for display. So don't confuse the date value per se and the date as it is displayed.
A Date/Time is always stored in the same way, no matter how it is formatted and displayed!
All you need to do is to open the Table in Design View and to set the Format property.
SELECT inv_no,NVL2(inv_amt,inv_date,'Not Available')
FROM invoice;
SELECT inv_no,NVL2(inv_amt,inv_amt*.25,'Not Available') FROM invoice;
getting
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-01722: invalid number
respectively
Please suggest what are the datatypes I can give in expr1 and expr2.
Also Please tell me how this is right?
SELECT inv_no,NVL2(inv_date,sysdate-inv_date,sysdate)
FROM invoice
The 2nd and 3rd parameters to NVL2 should be the same datatype. So, assuming inv_date is a DATE, you'd need to have that as a varchar2 like;
SELECT inv_no, NVL2(inv_amt, to_char(inv_date, 'dd-mon-yyyy hh24:mi:ss'), 'Not Available') FROM ...
or whatever format string you wanted. Otherwise Oracle will convert the 3rd parameter 'Not Available' to match the 2nd parameter's data type. This will try to convert 'Not Available' to a date and crash.
Similarly in the 2nd example, you have to convert the inv_amt*.25 to a char viato_char, e.g. to_char(inv_amt*.25).
Your first 2 examples attempt to have a date / numeric and text results for the same field. This will cause an error when Oracle attempts to convert this text to either types. You'll need to use the to_char(field) function on the date / numeric fields to convert them to text.
Lastly a date is in fact a number added to a databases base date. For example a date is a number of base day and the decimal it has is the ratio of a day, e.g. 0.5 is 12 hours, and the database has a base date, e.g. 01-Jan-1900 or 01-Jan-2000. This is why when you do date - date the result is a number and a date can also be represented as a number.
Refer to: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions106.htm
The 2nd argument should be either character or numeric. The 2nd argument gives datatype to which the 3rd argument will be implictly converted. So:
1. The first one SELECT inv_no,NVL2(inv_amt,inv_date,'Not Available') FROM invoice; is incorrect as you cannot have DATE as a 2nd argument.
The same way:
SELECT NVL2(NULLIF(1, 1), SYSDATE, 'A') FROM DUAL; -- FAILS;
SELECT NVL2(NULLIF(1, 0), SYSDATE, 'A') FROM DUAL; -- FAILS;
SELECT NVL2(NULLIF(1, 1), 'A', SYSDATE) FROM DUAL; -- PASS; returns SYSDATE (as char datatype)
SELECT NVL2(NULLIF(1, 0), 'A', SYSDATE) FROM DUAL; -- PASS; returns 'A';
2. The second is invalid too: SELECT inv_no,NVL2(inv_amt,inv_amt*.25,'Not Available') FROM invoice; Oracle tries to implicitly convert 3rd argument to the datatype of the 2nd arugment, meaning it tries to convert 'Not available' to numeric value.
It will work when you swap 2nd and 3rd argument:
SELECT inv_no,NVL2(inv_amt,'Not Available',inv_amt*.25) FROM invoice;
inv_amt*.25 will be implicitly converted to character data; This example is useless as it will get you NULL*.25 making it NULL.
What you can do however is you can make both parameters as characters by converting numeric result to character:
SELECT inv_no,NVL2(inv_amt,TO_CHAR(inv_amt*.25), 'Not Available') FROM invoice;
It will make some sense particularly when you want to display currency.
3. The third one is more complex
The only way I can explain is that the 2nd argument gets converted to character before dealing with the third one. That way the third one will get converted to characters too. This would make sense given the two cases:
SELECT NVL2(2, SYSDATE - TO_DATE('10-JAN-83'), SYSDATE) from dual; -- PASSES
SELECT NVL2(2, 2.2, SYSDATE) from dual; -- FAILS.
Finally: I would advise to stick to Oracle's documentation and use explicit conversion.
When ececute the following SQL syntax in Oracle, always not success, please help.
40284.3878935185 represents '2010-04-16 09:18:34', with microsecond.
an epoch date of 01 January 1900 (like Excel).
create table temp1 (date1 number2(5,10));
insert into temp1(date1) values('40284.3878935185');
select to_date(date1, 'yyyy-mm-dd hh24:mi:ssxff') from temp1
Error report: SQL 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.
Thanks to Mark Bannister
Now the SQL syntax is:
select to_char(to_date('1899-12-30','yyyy-mm-dd') +
date1,'yyyy-mm-dd hh24:mi:ss') from temp1
but can't fetch the date format like 'yyyy-mm-dd hh24:mi:ss.ff'. Continue look for help.
Using an epoch date of 30 December 1899, try:
select to_date('1899-12-30','yyyy-mm-dd') + date1
Simple date addition doesn't work with timestamps, at least if you need to preserve the fractional seconds. When you do to_timestamp('1899-12-30','yyyy-mm-dd')+ date1 (in a comment on Mark's answer) the TIMESTAMP is implicitly converted to a DATE before the addition, to the overall answer is a DATE, and so doesn't have any fractional seconds; then you use to_char(..., '... .FF') it complains with ORA-01821.
You need to convert the number of days held by your date1 column into an interval. Fortunately Oracle provides a function to do exactly that, NUMTODSINTERVAL:
select to_timestamp('1899-12-30','YYYY-MM-DD')
+ numtodsinterval(date1, 'DAY') from temp3;
16-APR-10 09.18.33.999998400
You can then display that in your desired format, e.g. (using a CTE to provide your date1 value):
with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')
+ numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SSXFF') from temp3;
2010-04-16 09:18:33.999998400
Or to restrict to thousandths of a second:
with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')+
+ numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SS.FF3') from temp3;
2010-04-16 09:18:33.999
An epoch of 1899-12-30 sounds odd though, and doesn't correspond to Excel as you stated. It seems more likely that your expected result is wrong and it should be 2010-04-18, so I'd check your assumptions. Andrew also makes some good points, and you should be storing your value in the table in a TIMESTAMP column. If you receive data like this though, you still need something along these lines to convert it for storage at some point.
Don't know the epoch date exactly, but try something like:
select to_date('19700101','YYYYMMDD')+ :secs_since_epoch/86400 from dual;
Or, cast to timestamp like:
select cast(to_date('19700101', 'YYYYMMDD') + :secs_since_epoch/86400 as timestamp with local time zone) from dual;
I hope this doesn't come across too harshly, but you've got to totally rethink your approach here.
You're not keeping data types straight at all. Each line of your example misuses a data type.
TEMP1.DATE1 is not a date or a varchar2, but a NUMBER
you insert not the number 40284.3878935185, but the STRING >> '40284.3878935185' <<
your SELECT TO_DATE(...) uses the NUMBER Temp1.Date1 value, but treats it as a VARCHAR2 using the format block
I'm about 95% certain that you think Oracle transfers this data using simple block data copies. "Since each Oracle date is stored as a number anyway, why not just insert that number into the table?" Well, because when you're defining a column as a NUMBER you're telling Oracle "this is not a date." Oracle therefore does not manage it as a date.
Each of these type conversions is calculated by Oracle based on your current session variables. If you were in France, where the '.' is a thousands separator rather than a radix, the INSERT would completely fail.
All of these conversions with strings are modified by the locale in which Oracle thinks your running. Check dictionary view V$NLS_PARAMETERS.
This gets worse with date/time values. Date/time values can go all over the map - mostly because of time zone. What time zone is your database server in? What time zone does it think you're running from? And if that doesn't spin your head quite enough, check out what happens if you change Oracle's default calendar from Gregorian to Thai Buddha.
I strongly suggest you get rid of the numbers ENTIRELY.
To create date or date time values, use strings with completely invariant and unambiguous formats. Then assign, compare and calculate date values exclusively, e.g.:
GOODFMT constant VARCHAR2 = 'YYYY-MM-DD HH24:MI:SS.FFF ZZZ'
Good_Time DATE = TO_DATE ('2012-02-17 08:07:55.000 EST', GOODFMT);
I'm sure this is quite simple, but I've been stuck on it for some time. How can I convert a varchar field (YYYYMM) to a date (MM/01/YY) in SQL?
Thanks.
Edit: I'm using Open Office Base (HSQL), not MySQL; sorry for the confusion.
Try the str_to_date and date_format functions. Something like:
select date_format( str_to_date( my_column, '%Y%c' ), '%c/01/%y' ) from my_table
try :
SELECT STR_TO_DATE(CONCAT(myDate,'01'),'%Y%m%d')
FROM myTable
Use STR_TO_DATE:
From mysql.com:
STR_TO_DATE(str,format)
This is the inverse of the DATE_FORMAT() function. It takes a string str and a format string format. STR_TO_DATE() returns a DATETIME value if the format string contains both date and time parts, or a DATE or TIME value if the string contains only date or time parts.
The date, time, or datetime values contained in str should be given in the format indicated by format. For the specifiers that can be used in format, see the DATE_FORMAT() function description. If str contains an illegal date, time, or datetime value, STR_TO_DATE() returns NULL. Starting from MySQL 5.0.3, an illegal value also produces a warning.
Range checking on the parts of date values is as described in Section 11.3.1, “The DATETIME, DATE, and TIMESTAMP Types”. This means, for example, that “zero” dates or dates with part values of 0 are allowed unless the SQL mode is set to disallow such values.
mysql> SELECT STR_TO_DATE('00/00/0000', '%m/%d/%Y');
-> '0000-00-00'
mysql> SELECT STR_TO_DATE('04/31/2004', '%m/%d/%Y');
-> '2004-04-31'
Get the year:
SUBSTRING(field FROM 2 FOR 2)
Get the month:
SUBSTRING(field FROM -2 FOR 2)
Compose the date:
CONCAT(SUBSTRING(field FROM -2 FOR 2), '/01/', SUBSTRING(field FROM 2 FOR 2))
This will convert from YYYYMM to MM/01/YY.
To be clear: if you're looking for method to convert some value of type Varchar/Text to value of type Date than solutions are:
using CAST function
CAST(LEFT('201205',4)||'-'||SUBSTRING('201205' FROM 5 FOR 6)||'-01' AS DATE)
starting from OpenOffice 3.4 (HSQLDB 2.x) new Oracle-like function TO_DATE supposed to be available
TO_DATE('201205','YYYYMM')
in addition to the written i can mention that you also can construct a string with ANSI/ISO 'YYYY-MM-DD' formatted representation of the date,- Base will acknowledge that and succesfully convert it to the Date type if necessary (e.g. INSERTing in Date typed column etc.)
Here is doc's on HyperSQL and highly recommended OO Base guide by Andrew Pitonyak