Question

Am trying to load some data to an Oracle database using SQLLoader but I keep getting the errors ORA-00932: inconsistent datatypes: expected DATE got NUMBER or expecting char got date

The date comes in the format of e.g. 20130815 and it is to be stored into a database with a column of type Date.

The logic should be that if someone passes an invalid date a null or say an invalid month in the date string e.g. 20131301, then I want to insert a default date e.g. 19990101.

Here is the SQLLoader code I have so far:

COLUMN_DOB   DATE 'YYYYMMDD' "CASE:COLUMN_DOB WHEN 'isdate(:COLUMN_DOB)=1' THEN :COLUMN_DOB ELSE to_date('19000101', 'YYYYMMDD') END",,
Was it helpful?

Solution

The immediate issue is that you're calling to_date() on the fixed value. The then part is returning the original string value of the date from the file; the else is returning a date; which leads to the 'expecting char got date' message.

If you remove the to_date() part then it will load all the values as 1900-01-01, because you have the case statement wrong. You're comparing the :COLUMN_DOB value with the string 'isdate(:COLUMN_DOB)=1'. It isn't calling the function, it's a fixed string, and your date field is never going to exactly match that text. So the case always goes into the else and gets the fixed value. You also seem to be mixing up the two forms of case statement.

So it should be:

... "CASE WHEN isdate(:COLUMN_DOB)=1 THEN :COLUMN_DOB ELSE '19000101' END"

Which, assuming you've built an isdate() function - since that is not an Oracle built-in - with a default format mask, something like this one based on an AskTom version:

create or replace function isdate (p_string in varchar2,
  p_fmt in varchar2 := 'YYYYMMDD')
return number as
  l_date date;
begin
  if l_date is null then
    return 0;
  end if;
  l_date := to_date(p_string, p_fmt);
  return 1;
exception
  when others then
     return 0;
end;
/

... will put in valid dates as supplied, and invalid dates as 1900-01-01. The null check also means nulls will be inserted as 1900-01-01; it's perhaps simpler to have it here than to try to handle that separately in the control file.

You could maybe simplify it further by having a function that tries to convert the string and returns a date, and just calling that in the control file, without the date mask or the case statement. That approach is covered in that AskTom link too.

Personally I'd probably prefer to see the column left null rather than giving it a magic number. It isn't impossible to have someone with a valid DOB of 1900-01-01.


If you are creating a new function anyway, you could do this instead:

create or replace function my2date(p_str in varchar2) return date is
begin
  return to_date(nvl(p_str, '19000101'), 'YYYYMMDD');
exception
  when others then -- just about acceptable here
    return date '1900-01-01';
end;
/

which you can execute from SQL*Plus, SQL Developer, Toad or whatever client you're using. And then you control file would have:

COLUMN_DOB "my2date(:COLUMN_DOB)"

I don't think there's a way of doing this without using a function. If you used an external table instead then you could use an anonymous block to do the conversion I suppose, but if you're stuck with SQL*Loader then I believe a function is the only way to stop a bad date causing the whole row to be rejected.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top