conversion into VARCHAR not possible when populating a table for records field [duplicate] - oracle

This question already has answers here:
ORA-1843: not a valid month while updating record
(1 answer)
A non-numeric character was found where a numeric was expected
(3 answers)
ORACLE - ORA-01843: not a valid month
(2 answers)
Closed 4 years ago.
I am using Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production and starting from 2 select statements:
select to_char('29.01.2018 00:00:00', 'DD.MM.YYYY HH24:MI:SS') from dual;
vs.
select to_char(sysdate, 'DD.MM.YYYY HH24:MI:SS') from dual;
I am asking why 1st select returns error: ORA-01722: invalid number?
Why 1st select, in order to return the expected result needs to be written as: select to_char(to_date('29.01.2018 00:00:00', 'DD.MM.YYYY HH24:MI:SS')) from dual;?
... and extrapolating from here I have another 2 situations related to 1st select:
when it populates a VARCHAR2 field from a record it works just fine.
when it populated a VARCHAR2 field from a table of records it returns above error message?
Thank you very much,

I am asking why 1st select returns error: ORA-01722: invalid number?
There are three versions of to_char() which take different data type arguments; one for number, one for datetime and one for char.
If you only passed your string, without the second format-model argument:
to_char('29.01.2018 00:00:00')
then it would use the char version and not complain. But you are supplying a second argument, so only the number and datetime versions might be valid.
Neither of those accepts text literals as the first argument. But Oracle generally tries to be helpful and allows a lot of implicit conversion. (This isn't always a good thing.)
In this case it assumes you meant the first version and are trying to pass in a number, and attempts to implicitly convert the string you gave to a number. As it can't be converted, you get the ORA-01722 error.
When you modify it to do:
to_char(to_date('29.01.2018 00:00:00', 'DD.MM.YYYY HH24:MI:SS'))
you're explicitly converting the string to a datetime, so it knows you want to use that version of the function, not the number version, since that is the data type of the first argument when the function is actually called..

to_char('29.01.2018 00:00:00', 'DD.MM.YYYY HH24:MI:SS') is useless. '29.01.2018 00:00:00' is a STRING, not a DATE. It does not make any sense to convert a string into a string.
In order to output a date in certain format with TO_CHAR() you have to provide a DATE (or TIMESTAMP) value. SYSDATE is a DATE value.
One way to provide a DATE is using TO_DATE() function as given in your question.

Related

Insert SYSTIMESTAMP for Timestamp field

I have three timestamps in my SQL Table.
Column Name Data Type Nullable Data_Default
STATUS_TIMSTM TIMESTAMP(6) No (null)
CREATED_TIMSTM TIMESTAMP(6) No SYSTIMESTAMP
UPDATED_TIMSTM TIMESTAMP(6) No (null)
INSERT INTO "TABLE_NAME" ("STATUS_TIMSTM","CREATED_TIMSTM","UPDATED_TIMSTM")
VALUES(TIMESTAMP '2020-12-10 00:00:00', TIMESTAMP '2020-06-15 00:00:00',TIMESTAMP '2020-06-15 00:00:00');
The above works correctly.
How do I insert the current systimestamp?
I've tried several options: curdate(), now(), systimestamp().
I usually get errors such as Error report -
SQL Error: ORA-00904: "NOW": invalid identifier 00904. 00000 - "%s: invalid identifier"
You should be able to use current_timestamp:
create table t (x TIMESTAMP(6));
insert into t (x) values (current_timestamp);
Of course, systimestamp should also work.
Here is a db<>fiddle.
Since you already have a DATA DEFAULT, only inserting data in below format must populate the CREATED_TIMSTM column with current TIMESTAMP.
INSERT INTO "TABLE_NAME" ("STATUS_TIMSTM","UPDATED_TIMSTM")
VALUES(TIMESTAMP '2020-12-10 00:00:00', TIMESTAMP '2020-06-15 00:00:00');
Here is a simplified DB fiddle demonstrating the same.
In Oracle you would
insert into my_table(timestamp_column) values (systimestamp);
Notice that the function call does not include parentheses after the function name. Oracle is pretty odd in this regard; functions that don't take parameters, but that you define yourself, must use empty parentheses, but similar functions (no parameters) that are provided by Oracle must be used without parentheses. Only Oracle knows why it's inconsistent this way. This explains why your attempt was failing.
(Actually, some experimentation with systimestamp shows that it can take an argument - a positive integer which shows how many decimal places you want for seconds! In any case, you can't use it with empty parentheses.)
There are other "current" timestamp functions, but they do different things. systimestamp returns the timestamp of the computer system that hosts the database server. (Note that this may, and often is, different from the database timestamp.) In any case, systimestamp is by far the most commonly used of these; similar to sysdate for dates.
Beware of time zone though. systimestamp returns timestamp with time zone. By inserting it into a timestamp column, you are losing information. Is that OK for your business application?

Date operations in Oracle

I'm trying to run this queries (Oracle 12c):
SELECT trunc(sysdate) - '25-SEP-18' FROM dual;
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
I expect implicit conversion and everything is good with the 2 last queries, but for the first i get error ORA-01722: invalid number. NLS_DATE_FORMAT = 'DD-MON-RR'. What is the problem?
The question is WHY is does not work? I didn't find any explanations in documentation.
The documentation has a section on Datetime/Interval Arithmetic which explains what is allowed. The table shows that arithmetic is only allowed between dates, timestamp, intervals and numbers. When you do:
SELECT trunc(sysdate) - '25-SEP-18'
you are trying to subtract a string from a date, which isn't possible. Oracle 'helpfully' tries anyway and interprets the string as a number, effectively doing:
SELECT trunc(sysdate) - to_number('25-SEP-18')
which understandably throws the error you see, "ORA-01722: invalid number". As already said, you should explicitly convert your string to a date:
SELECT trunc(sysdate) - to_number('25-SEP-18', 'DD-MON-RR')
or preferably with a four-digit year, and since you're using a month name it's safer to specify the language that is in:
SELECT trunc(sysdate) - to_number('25-SEP-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH')
or more simply, if it's a fixed value, with a date literal:
SELECT trunc(sysdate) - DATE '2018-09-25'
I expect implicit conversion
You should not rely on implicit conversion, particularly where that is influenced by session NLS settins. As well as the date language I already mentioned, someone else running your statement could have a different NLS_DATE_FORMAT setting which could lead to errors or more subtle data mismatches or corruption; e.g.
alter session set nls_date_format = 'DD-MON-YYYY';
SELECT trunc(sysdate) - DATE '2018-09-25' FROM dual;
TRUNC(SYSDATE)-DATE'2018-09-25'
-------------------------------
344
SELECT trunc(sysdate) - to_date('25-SEP-18') FROM dual;
TRUNC(SYSDATE)-TO_DATE('25-SEP-18')
-----------------------------------
730831
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
no rows selected
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
The second query gets a much bigger value than expected; and the third gets no rows back from dual.
Looking at the implicitly converted date shows you why:
SELECT to_char(order_date, 'SYYYY-MM-DD HH24:MI:SS') FROM my_table;
TO_CHAR(ORDER_DATE,'
--------------------
0019-09-04 00:00:00
With a YYYY mask (and no FX modifier) a 2-digit year value like 19 is converted as 0019, not 2019. That sort of problem could go unnoticed for some time, giving you incorrect results in the meantime.
If the session's format mask had RRRR or - as you have - RR then it would be interpreted as 2019; but the point is that you usually have no control over the settings in another session that runs your code later.
You can also cause performance issues or errors by creating implicit conversions where you didn't expect, or where they behave in a way you didn't expect. Not in this example - "When comparing a character value with a DATE value, Oracle converts the character data to DATE" - but it still comes up. It's better to avoid the possibility.
When dealing with strings with dates in them you should use the to TO_DATE command, otherwise Oracle may not always figure out that the string contains a date.
SELECT trunc(sysdate) - TO_DATE('25-SEP-18') FROM dual;
Even better is to indicate the format of the date within the string
SELECT trunc(sysdate) - TO_DATE('25-SEP-18','DD-MON-RR') FROM dual;

How to change default date,timestamp dataype for columns in oracle

I have created a table in Oracle in which I have KPI_START_DATE column which is a Date datatype, and KPI_START_TIME which is a TIMESTAMP datatype.
Now I want to modify this date dataype for
KPI_START_DATE to dd/mm/yyyy
and
KPI_START_TIME to HH:MI:SS.
So that user should always enter the date and time in this column in this proper format.
I tried below query but its was giving error:
Alter table KPI_DEFINITION MODIFY(to_char(KPI_START_DATE,'dd/mm/yyyy') )
DATE and TIMESTAMP columns do not have any inherent readable format. The values are stored in Oracle's own internal representation, which has no resemblance to a human-readable date or time. At the point to retrieve or display a value you can convert it to whatever format you want, with to_char().
Both DATE and TIMESTAMP have date and time components (to second precision with DATE, and with fractional seconds with TIMESTAMP; plus time zone information with the extended data types), and you should not try to store them separately as two columns. Have a single column and extract the information you need at any time; to get the information out of a single column but split into two fields you could do:
select to_char(KPI_START, 'dd/mm/yyyy') as KPI_START_DATE,
to_char(KPI_START, 'hh24:mi:ss') as KPI_START_TIME
but you'd generally want both together anyway:
select to_char(KPI_START, 'dd/mm/yyyy hh24:mi:ss')
Also notice the 'hh24' format model to get the 24-hour clock time; otherwise you wouldn't see any difference between 3 a.m. and 3 p.m.
You can store a value in either type of column with the time set to midnight, but it does still have a time component - it is just midnight. You can't store a value in either type of column with just a time component - it has to have a date too. You could make that a nominal date and just ignore it, but I've never seen a valid reason to do that - you're wasting storage in two columns, and making searching for and comparing values much harder. Oracle even provides a default date if you don't specify one (first day of current month). But the value always has both a date and a time part:
create table KPI_DEFINITION (KPI_START date);
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015', 'DD/MM/YYYY'));
insert into KPI_DEFINITION (KPI_START)
values (to_date('12:41:57', 'HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 00:00:00
2015-01-01 12:41:57
Your users should be inserting a single value with both date and time as one:
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015 12:41:57', 'DD/MM/YYYY HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 12:41:57
You can also use date or timestamp literals, and if using to_date() you should always specify the full format - don't rely on NLS settings as they may be different for other users.
You should understand difference between datatype and format. DATE is a datatype. TIMESTAMP is a datatype. None of them have formats, they're just numbers.
When converting character datatype to or from date datatype, format should be applied. It's an attribute of an actual conversion, nothing else.
Look at this:
SQL> create table tmp$date(d date);
Table created
SQL> insert into tmp$date values (DATE '2010-11-01');
1 row inserted
SQL> insert into tmp$date values (DATE '2014-12-28');
1 row inserted
SQL> select d, dump(d) from tmp$date;
D DUMP(D)
----------- ---------------------------------
01.11.2010 Typ=12 Len=7: 120,110,11,1,1,1,1
28.12.2014 Typ=12 Len=7: 120,114,12,28,1,1,1
There is no any 'format' here.
DISPLAYING and STORING are NOT the same when it comes to DATE.
When people say Oracle isn’t storing the date in the format they wanted, what is really happening is Oracle is not presenting the date in the character string format they expected or wanted.
When a data element of type DATE is selected, it must be converted from its internal, binary format, to a string of characters for human consumption. The conversion of data from one type to another is known as known a “conversion”, “type casting” or “coercion”. In Oracle the conversion between dates and character strings is controlled by the NLS_DATE_FORMAT model. The NLS_DATE_FORMAT can be set in any of several different locations, each with its own scope of influence.
I could go on with my leacture over DATE data type, but I am glad that someone has already got a good writeup over this. Please read this https://edstevensdba.wordpress.com/2011/04/07/nls_date_format/

Confusion using to_date function to confirm date format

select to_date('07/09/14','yyyy-mm-dd') from dual;
is returning 14-SEP-07
I was expecting it to thrown an exception as the date and the format requested are not the same. Secondly we have slashes in the input date and hypen in the format.
Can someone tell me how to confirm if the input value is of the provided format.
to_date is relatively liberal in attempting to convert the input string using the provided format mask. It generally doesn't concern itself with the specific separator character in the string or in the format mask-- your string could use dashes or slashes or, heck, asterixes if you wanted. Of course, that can mean that you get unexpected results. In this case, for example, the date that is created is in the year 7 (i.e. 2007 years ago). That is a valid date in Oracle but it is highly unlikely to be the date you expect unless you're storing data about ancient Rome.
What, exactly, does it mean to you to "confirm if the input value is of the provided format"? Depending on what you are looking for, you may want to use regular expressions.
REGEXP_LIKE can somewhat do this. Note, this is basic, since it would accept values like 2014-0-32. However, those insane values would fail in whatever you do next in your code, such as to_date().
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('07/09/14','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
no rows selected
.
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('2014-07-09','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IS_VALID
----------------
Yes, valid boss.
.
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('2014-7-9','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IS_VALID
----------------
Yes, valid boss.
EDIT: and if you're into PL/SQL, you can do a regex match and throw your own exception ...
DECLARE
v_is_valid INTEGER;
BEGIN
SELECT count(*) INTO v_is_valid FROM DUAL
WHERE regexp_like('07/09/14','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IF v_is_valid = 0 THEN
raise_application_error (-20400, 'Exception: date was given in the wrong format.');
END IF;
END;
/
*
ORA-20400: Exception: date was given in the wrong format.
TO_DATE() only takes a string and convert it to a date/time in respect to the given format your provided. If it can't find the exact format, it will do its best to determine what could it be. Given your 'yyyy-mm-dd' format, Oracle deduces that the 07 is the year, and the 14 is the day.
You have to write your own function to throw an expected exception or the like to handle the issue, if there is any based on your functional requirements.
So when you select the resulting date, the format that is displayed is actually based on your NLS_DATE_FORMAT system parameter, which gives your resulting date.

Oracle date corruption during update

I'm migrating some data from one oracle schema/table to a new schema/table on the same database.
The migration script does the following:
create table newtable as select
...
cast(ACTIVITYDATE as date) as ACTIVITY_DATE,
...
FROM oldtable where ACTIVITYDATE > sysdate - 1000;
If I look at the original data, it looks fine - here's one record:
select
activitydate,
to_char(activitydate, 'MON DD,YYYY'),
to_char(activitydate, 'DD-MON-YYYY HH24:MI:SS'),
dump(activitydate),
length(activitydate)
from orginaltable where oldpk = 1067514
Result:
18-NOV-10 NOV 18,2010 18-NOV-2010 12:59:15 Typ=12 Len=7: 120,110,11,18,13,60,16
The migrated data, showing that the data is corrupt:
select
activity_date,
to_char(activity_date, 'MON DD,YYYY'),
to_char(activity_date, 'DD-MON-YYYY HH24:MI:SS'),
dump(activity_date),
length(activity_date)
from newtable
where id = 1067514
Result:
18-NOV-10 000 00,0000 00-000-0000 00:00:00 Typ=12 Len=7: 120,110,11,18,13,0,16
Around 5000 out of 350k records show this problem.
Can anyone explain how this happened?
UPDATE:
I don't find any published reference to this specific type of DATE corruption on the Oracle support site. (It may be there, my quick searches just didn't turn it up.)
Baddate Script To Check Database For Corrupt dates [ID 95402.1]
Bug 2790435 - Serial INSERT with parallel SELECT and type conversion can insert corrupt data [ID 2790435.8]
The output from the DUMP() function is showing the date value is indeed invalid:
Typ=12 Len=7: 120,110,11,18,13,0,16
We expect that the minutes byte should be a value between one and sixty, not zero.
The 7 bytes of a DATE value represent, in order, century(+100), year(+100), month, day, hour(+1), minutes(+1), seconds(+1).
The only time I have seen invalid DATE values like this when a DATE value was being supplied as a bind variable, from a Pro*C program (where the bind value is supplied in the internal 7 byte representation, entirely bypassing the normal validation routines that catch invalid dates e.g. Feb 30)
There is no reason to expect the behavior you're seeing, given the Oracle syntax you posted.
This is either a spurious anomaly (memory corruption?) or if this is repeatable, then it's a flaw (bug) in the Oracle code. If it's a flaw in the Oracle code, the most likely suspects would be "newish" features in an un-patched release.
(I know CAST is a standard SQL function that's been around for ages in other databases. I guess I'm old school, and have never introduced it into my Oracle-syntax repertoire. I don't know what version of Oracle it was that introduced the CAST, but I would have stayed away from it in the first release it appeared in.)
The big 'red flag' (that another commenter noted) is that CAST( datecol AS DATE).
You would expect the optimizer to treat that as equivalent to date_col ... but past experience shows us that TO_NUMBER( number_col ) is actually interpreted by the optimizer as TO_NUMBER( TO_CHAR ( number_col ) ).
I suspect something similar might be going on with that unneeded CAST.
Based on that one record you showed, I suspect the issue is with values with a "59" value for minutes or seconds, and possibly a "23" value for hours, would be the ones that show the error.
I would try checking for places where the minutes, hour or seconds are stored as 0:
SELECT id, DUMP(activitydate)
FROM newtable
WHERE DUMP(activitydate) LIKE '%,0,%'
OR DUMP(activitydate) LIKE '%,0'
I've seen similar things to spence7593, again with Pro*C.
It is possible to create invalid dates programmatically using a DBMS_STATS package.
Not sure if there is a similar mechanism to reverse that.
create or replace function stats_raw_to_date (p_in raw) return date is
v_date date;
v_char varchar2(25);
begin
dbms_stats.CONVERT_RAW_VALUE(p_in, v_date);
return v_date;
exception
when others then return null;
end;
/
select stats_raw_to_date(utl_raw.cast_to_raw(
chr(120)||chr(110)||chr(11)||chr(18)||chr(13)||chr(0)||chr(16)))
from dual;

Resources