Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005

rekharaghuram
from rekharaghuram More from this publisher
05.11.2015 Views

CHAPTER 15 ■ DATA LOADING AND UNLOADING 661 10,Sales,Virginia,1/5/2000 20,Accounting,Virginia,21/6/1999 30,Consulting,Virginia,5/1/2000 40,Finance,Virginia,15/3/2001 you would find this error in your log file, for each input record: Record 1: Rejected - Error on table DEPT, column ENTIRE_LINE. Column not found before end of logical record (use TRAILING NULLCOLS) Here, SQLLDR is telling you that it ran out of data in the record before it ran out of columns. The solution is easy in this case, and, in fact, SQLLDR even tells us what to do: use TRAILING NULLCOLS. This will have SQLLDR bind a NULL value in for that column if no data exists in the input record. In this case, adding TRAILING NULLCOLS will cause the bind variable :ENTIRE_LINE to be NULL. So, you retry with this control file: LOAD DATA INFILE * INTO TABLE DEPT REPLACE FIELDS TERMINATED BY ',' TRAILING NULLCOLS (DEPTNO, DNAME "upper(:dname)", LOC "upper(:loc)", LAST_UPDATED date 'dd/mm/yyyy', ENTIRE_LINE ":deptno||:dname||:loc||:last_updated" ) BEGINDATA 10,Sales,Virginia,1/5/2000 20,Accounting,Virginia,21/6/1999 30,Consulting,Virginia,5/1/2000 40,Finance,Virginia,15/3/2001 Now the data in the table is as follows: ops$tkyte@ORA10G> select * from dept; DEPTNO DNAME LOC ENTIRE_LINE LAST_UPDA ------ -------------- ------------- ----------------------------- --------- 10 SALES VIRGINIA 10SalesVirginia1/5/2000 01-MAY-00 20 ACCOUNTING VIRGINIA 20AccountingVirginia21/6/1999 21-JUN-99 30 CONSULTING VIRGINIA 30ConsultingVirginia5/1/2000 05-JAN-00 40 FINANCE VIRGINIA 40FinanceVirginia15/3/2001 15-MAR-01 What makes this feat possible is the way SQLLDR builds its INSERT statement. SQLLDR will look at the preceding and see the DEPTNO, DNAME, LOC, LAST_UPDATED, and ENTIRE_LINE columns in the control file. It will set up five bind variables named after these columns. Normally, in the absence of any functions, the INSERT statement it builds is simply

662 CHAPTER 15 ■ DATA LOADING AND UNLOADING INSERT INTO DEPT ( DEPTNO, DNAME, LOC, LAST_UPDATED, ENTIRE_LINE ) VALUES ( :DEPTNO, :DNAME, :LOC, :LAST_UPDATED, :ENTIRE_LINE ); It would then parse the input stream, assigning the values to its bind variables, and then execute the statement. When you begin to use functions, SQLLDR incorporates them into its INSERT statement. In the preceding example, the INSERT statement SQLLDR builds will look like this: INSERT INTO T (DEPTNO, DNAME, LOC, LAST_UPDATED, ENTIRE_LINE) VALUES ( :DEPTNO, upper(:dname), upper(:loc), :last_updated, :deptno||:dname||:loc||:last_updated ); It then prepares and binds the inputs to this statement, and executes it. So, pretty much anything you can think of doing in SQL, you can incorporate into your SQLLDR scripts. With the addition of the CASE statement in SQL, doing this can be extremely powerful and easy. For example, say your input file could have dates in the following formats: • HH24:MI:SS: Just a time; the date should default to SYSDATE. • DD/MM/YYYY: Just a date; the time should default to midnight. • HH24:MI:SS DD/MM/YYYY: The date and time are both explicitly supplied. You could use a control file like this: LOAD DATA INFILE * INTO TABLE DEPT REPLACE FIELDS TERMINATED BY ',' TRAILING NULLCOLS (DEPTNO, DNAME "upper(:dname)", LOC "upper(:loc)", LAST_UPDATED "case when length(:last_updated) > 9 then to_date(:last_updated,'hh24:mi:ss dd/mm/yyyy') when instr(:last_updated,':') > 0 then to_date(:last_updated,'hh24:mi:ss') else to_date(:last_updated,'dd/mm/yyyy') end" ) BEGINDATA 10,Sales,Virginia,12:03:03 17/10/2005 20,Accounting,Virginia,02:23:54 30,Consulting,Virginia,01:24:00 21/10/2005 40,Finance,Virginia,17/8/2005

662<br />

CHAPTER 15 ■ DATA LOADING AND UNLOADING<br />

INSERT INTO DEPT ( DEPTNO, DNAME, LOC, LAST_UPDATED, ENTIRE_LINE )<br />

VALUES ( :DEPTNO, :DNAME, :LOC, :LAST_UPDATED, :ENTIRE_LINE );<br />

It would then parse the input stream, assigning the values to its bind variables, <strong>and</strong> then<br />

execute the statement. When you begin to use functions, SQLLDR incorporates them into its<br />

INSERT statement. In the preceding example, the INSERT statement SQLLDR builds will look<br />

like this:<br />

INSERT INTO T (DEPTNO, DNAME, LOC, LAST_UPDATED, ENTIRE_LINE)<br />

VALUES ( :DEPTNO, upper(:dname), upper(:loc), :last_updated,<br />

:deptno||:dname||:loc||:last_updated );<br />

It then prepares <strong>and</strong> binds the inputs to this statement, <strong>and</strong> executes it. So, pretty much<br />

anything you can think of doing in SQL, you can incorporate into your SQLLDR scripts. With<br />

the addition of the CASE statement in SQL, doing this can be extremely powerful <strong>and</strong> easy. For<br />

example, say your input file could have dates in the following formats:<br />

• HH24:MI:SS: Just a time; the date should default to SYSDATE.<br />

• DD/MM/YYYY: Just a date; the time should default to midnight.<br />

• HH24:MI:SS DD/MM/YYYY: The date <strong>and</strong> time are both explicitly supplied.<br />

You could use a control file like this:<br />

LOAD DATA<br />

INFILE *<br />

INTO TABLE DEPT<br />

REPLACE<br />

FIELDS TERMINATED BY ','<br />

TRAILING NULLCOLS<br />

(DEPTNO,<br />

DNAME "upper(:dname)",<br />

LOC<br />

"upper(:loc)",<br />

LAST_UPDATED<br />

"case<br />

when length(:last_updated) > 9<br />

then to_date(:last_updated,'hh24:mi:ss dd/mm/yyyy')<br />

when instr(:last_updated,':') > 0<br />

then to_date(:last_updated,'hh24:mi:ss')<br />

else to_date(:last_updated,'dd/mm/yyyy')<br />

end"<br />

)<br />

BEGINDATA<br />

10,Sales,Virginia,12:03:03 17/10/2005<br />

20,Accounting,Virginia,02:23:54<br />

30,Consulting,Virginia,01:24:00 21/10/2005<br />

40,Finance,Virginia,17/8/2005

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!