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.