Although I wrote hundreds of SQL*Loader control files over the years, yesterday i came across one of those trivial situations that takes hours to be sorted out because of some tiny detail.
The task: to load a text file containing fixed-length fields and numeric fields are formatted using a postfix notation, minus sign for negative numbers or a blank for positive numbers.
Piece of cake i thought, i can format the field using an inline SQL function like "TO_NUMBER(:COL2, '999999MI')" as in:
load dataAnd here is what i found in the SQL*Loader log file:
infile *
truncate
into table test_table
(
COL1 POSITION(01:19) CHAR,
COL2 POSITION(20:26) CHAR "TO_NUMBER(:COL2,'999999MI')",
COL3 POSITION(27:27) CHAR
)
BEGINDATA
THIS ROW IS OK 123456-Y
THIS ROW IS NOT OK!234567 Y
THIS ROW IS NOT OK!012345 Y
The corresponding rows were also recorded in the badfile.
SQL*Loader: Release 10.2.0.1.0 - Production on Mon Aug 18 23:21:39 2008
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Control File: test.ctl
Data File: test.ctl
Bad File: test.bad
Discard File: none specified
(Allow all discards)
Number to load: ALL
Number to skip: 0
Errors allowed: 50
Bind array: 64 rows, maximum of 256000 bytes
Continuation: none specified
Path used: Conventional
Table TEST_TABLE, loaded from every logical record.
Insert option in effect for this table: TRUNCATE
Column Name Position Len Term Encl Datatype
------------------------------ ---------- ----- ---- ---- ---------------------
COL1 1:19 19 CHARACTER
COL2 20:26 7 CHARACTER
SQL string for column : "TO_NUMBER(:COL2,'999999MI')"
COL3 27:27 1 CHARACTER
Record 2: Rejected - Error on table TEST_TABLE, column COL2.
ORA-01722: invalid number
Record 3: Rejected - Error on table TEST_TABLE, column COL2.
ORA-01722: invalid number
Table TEST_TABLE:
1 Row successfully loaded.
2 Rows not loaded due to data errors.
0 Rows not loaded because all WHEN clauses were failed.
0 Rows not loaded because all fields were null.
Space allocated for bind array: 2304 bytes(64 rows)
Read buffer bytes: 1048576
Total logical records skipped: 0
Total logical records read: 3
Total logical records rejected: 2
Total logical records discarded: 0
Run began on Mon Aug 18 23:21:39 2008
Run ended on Mon Aug 18 23:21:40 2008
Elapsed time was: 00:00:00.54
CPU time was: 00:00:00.12
I must admit that this problem has tortured me for a couple of hours, before i finally got the "inspiration".
Eventually i realized that i had a problem with the blanks when i managed to re-create the error condition using a simple query. In particular i noticed that one can perform the following SELECT without problems:
select to_number('000000 ','999999MI') t from dual;but one gets ORA-01722 when the trailing blank is trimmed:
T
-
0
select to_number('000000','999999MI') t from dual;The main problem is in the fact that SQL*Loader by default ignores blanks or better said it strips them out altogether. Blank trimming is somewhat documented in the SQL*Loader Reference manual, but it's easy to overlook such fundamental information.
ORA-01722: invalid number
At this point i had a strong suspect on the fact that SQL*Loader was removing the trailing blank, regardless of the fixed-length specification, so i decided to enable the PRESERVE BLANKS option at the file level:
load dataAnd this time it worked without a hitch.
infile *
truncate
preserve blanks
into table test_table
(
COL1 POSITION(01:19) CHAR,
COL2 POSITION(20:26) CHAR "TO_NUMBER(:COL2,'999999MI')",
COL3 POSITION(27:27) CHAR
)
BEGINDATA
THIS ROW IS OK 123456-Y
THIS ROW IS NOW OK 234567+Y
THIS ROW IS NOW OK 012345 Y
Well, almost, first i had to sort out a side-effect of PRESERVE BLANKS, because in my real-life scenario i had also a trailing numeric column that started complaining about the presence of blanks.
So i modified the field definition to include NULLIF field = BLANKS and after doing that my control file was finally ok.
Edited on Tuesday 19:
note also that the MI numerical format allows to specify either a blank or a plus (+) sign indifferently.
I inserted a plus sign in line 2 of the last datafile version to clarify this point.
6 comments:
I am facing the same problem, but your solution doesn't work here. I have a number field (yes, it's defined just as NUMBER, nothing else), and the input has the format of 1542,12 or just 642 for example. The numeric characters are . and , to make things more funny. I don't get it :( nothing works ARgggHHhh!!!! I am getting crazy!
Andre, probably you are facing a slightly different problem that looks the same but it isn't.
It would be a good idea to provide the DDL for the table to be loaded, a couple of lines of the data file as well as the source of the control file to start off, otherwise it's like guessing numbers using a crystal ball.
Flavio
Hi,
I'm facing a similar problem where a .dat file gets loaded to Oracle and the ctl file looks like...
into table tmp_archive_tbl
when (1) != '*'
fields terminated by ',' optionally enclosed by '"'
( mailing_id POSITION(01:10) INTEGER EXTERNAL,
profile_id POSITION(12:21) INTEGER EXTERNAL,
created_tm POSITION(23:41) DATE "YYYYMMDDHH24:MI:SS",
source_code POSITION(43:52) CHAR,
content_code POSITION(54:63) CHAR,
action_code POSITION(65:74) CHAR,
-- subaction_code POSITION(76:85) CHAR,
subaction_code POSITION(176:185) CHAR,
tl_module_id POSITION(87:96) INTEGER EXTERNAL,
rule_bit_seq POSITION(98:117) INTEGER EXTERNAL,
load_job_id POSITION(119:128) INTEGER EXTERNAL,
unique_flg POSITION(130:130) INTEGER EXTERNAL,
ts_segment_id POSITION(23:32) CHAR,
wave_id POSITION(164:174) )
When wave_id is a negative number, it gets inserted into the tbl but the log file says that error is on subsequent line. Always the same.
value used for ROWS parameter changed from 1000 to 400
Record 2: Rejected - Error on table TMP_ARCHIVE_TBL, column MAILING_ID.
ORA-01722: invalid number
Record 3: Rejected - Error on table TMP_ARCHIVE_TBL, column MAILING_ID.
ORA-01722: invalid number
The .dat file has ...
36564788 1597888538 2010091714:29:40 RPLY H B 1 0 0 415089132 1 0 pmonitor3 -3550397807 OTHR 6540121 -3550397807
36564788 1597888538 2010091714:29:40 RPLY H B 1 0 0 415089132 0 0 pmonitor3 -3550397807 OTHR 6540121 -3550397807
36564788 1597888538 2010091714:29:41 RPLY H B 1 0 0 415089132 0 0 pmonitor3 -3550397807 OTHR 6540121 -3550397807
It works when wave_id is positive.
Also I am able to insert the '-3550397807' manually in Oracle...
SQL> insert into tmp_archive_tbl (wave_id) values ('-3550397807');
1 row created.
SQL> select wave_id from tmp_archive_tbl where wave_id like '%550%';
WAVE_ID
----------
-3.550E+09
What could I change to the ctl file to make this work? Thanks!
I believe this is a bug I've already seen some time ago on Oracle 10gR2.
It was somewhat elusive, the only way to get rid of it was to set BINDSIZE equal to the record length, including the newline character(s). I could accept this because the system was not loading big files.
If setting BINDSIZE to a single record size fixes the problem, I'd suggest to make further experiments with multiples, as the performance of the data load will be adversely affected if processing one line at a time, especially for large data loads.
If this doesn't solve the problem, you might want to send me the DDL for creating the table, the whole control file and tell me the database version you are using.
Flavio
Thanks for your blog! I had the same error, and though your solution wasn't the one I needed it led me to it. I'd created a csv file from an excel spreadsheet and ftp'd it to the unix server. The last column was a not null number column. All the data was numeric. But the records were rejected on the last column with 'ORA-01722: invalid number'
WHAT?!!
Apparently there were still line returns in the data file though I couldn't see them when I viewed the file in unix. The solution was to run dos2unix on the file.
Hi All,
Our requirement is ,
1) we have a default value for a CLOB column (For Ex : '1234')
2) In our Input file, we have some column values that are empty.
For Ex :
Name(varchar) Value(clob)
a1 clob1
a2
a3 clob3
3) Our control file is such that when imported above csv using SQL-LOADER,our data in DB should be as below
Name Value
a1 clob1
a2 1234
a3 clob3
How can we achieve this ?
I read SQL strings are not supported on CLOB.
Is there any SQL-LOADER specific expressions/procedure to achieve this?
Please help on this.
Post a Comment