我们可以用Oracle的sqlldr工具来导入数据:

Sqlldr userid=lgone/tiger control=a.ctl

着重关注以下几个参数:

userid
control -- 控制文件,可能包含表的数据
log
bad
data
errors
rows
skip

有两种使用方法:

(1)使用一个控制文件(作为模板) 和一个数据文件

一般为了利于模板和数据的分离,以及程序的不同分工会使用第一种方式,下面是一个比较完整的例子:



OPTIONS (skip=1,rows=128) -- sqlldr 命令显示的选项可以写到这里边来,skip=1 用来跳过数据中的第一行
LOAD DATA
INFILE "users_data.csv" --指定外部数据文件,可以写多个 INFILE "another_data_file.csv" 指定多个数据文件
// INFILE 'tt.date' // 导入多个文件 
--这里还可以使用 BADFILE、DISCARDFILE 来指定坏数据和丢弃数据的文件,
truncate --操作类型,用 truncate table 来清除表中原有记录
INTO TABLE users -- 要插入记录的表
Fields terminated by "," -- 数据中每行记录用 "," 分隔
Optionally enclosed by '"' -- 数据中每个字段用 '"' 框起,比如字段中有 "," 分隔符时
trailing nullcols --表的字段没有对应的值时允许为空
(
  virtual_column FILLER, --这是一个虚拟字段,用来跳过由 PL/SQL Developer 生成的第一列序号
  user_id number, --字段可以指定类型,否则认为是 CHARACTER 类型, log 文件中有显示
  user_name,
  login_times,
  last_login DATE "YYYY-MM-DD HH24:MI:SS" -- 指定接受日期的格式,相当用 to_date() 函数转换
)



其中append为数据装载方式,还有其他选项:    
  a、insert,为缺省方式,在数据装载开始时要求表为空    
  b、append,在表中追加新记录    
  c、replace,删除旧记录(用 delete from table 语句),替换成新装载的记录    
  d、truncate,删除旧记录(用 truncate table 语句),替换成新装载的记录  

(2)只使用一个控制文件,在这个控制文件中包含数据

把 users_data.cvs 中的内容补到 users.ctl 中,并以 BEGINDATA 连接,还要把 INFILE "users_data.csv" 改为 INFILE *。同时为了更大化的说明问题,把数据处理了一下。此时,完整的 users.ctl 文件内容是:



OPTIONS (skip=1,rows=128) -- sqlldr 命令显示的选项可以写到这里边来,skip=1 用来跳过数据中的第一行
LOAD DATA
INFILE *  -- 因为数据同控制文件在一起,所以用 * 表示
append    -- 这里用了 append 来操作,在表 users 中附加记录 
INTO TABLE users
when LOGIN_TIMES<>'8'  -- 还可以用 when 子句选择导入符合条件的记录
Fields terminated by ","
trailing nullcols
(
  virtual_column FILLER, --跳过由 PL/SQL Developer 生成的第一列序号
  user_id "user_seq.nextval", --这一列直接取序列的下一值,而不用数据中提供的值
  user_name "'Hi '||upper(:user_name)",--,还能用SQL函数或运算对数据进行加工处理
  login_times terminated by ",", NULLIF(login_times='NULL') --可为列单独指定分隔符
  last_login DATE "YYYY-MM-DD HH24:MI:SS" NULLIF (last_login="NULL") -- 当字段为"NULL"时就是 NULL
)
BEGINDATA --数据从这里开始
   ,USER_ID,USER_NAME,LOGIN_TIMES,LAST_LOGIN
1,1,Unmi,3,2009-1-5 20:34
2,2,Fantasia,5,2008-10-15
3,3,隔叶黄莺,8,2009-1-2
4,4,Kypfos,NULL,NULL
5,5,不知秋,1,2008-12-23



逐一举例说明:

1、普通装载

注:BEGINDATA后的数值前面不能有空格



LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'  
(DEPTNO,
 DNAME,
 LOC
)
BEGINDATA
10,Sales,"""USA"""
20,Accounting,"Virginia,USA" 
30,Consulting,Virginia
40,Finance,Virginia
50,"Finance","",Virginia --loc 列将为空
60,"Finance",,Virginia   --loc 列将为空



 2、TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY x'09' 的情况 

注:x'09'表示字符ASCII码的16进制数值,为tab分隔符

TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' :装载这种数据: 10,lg,"""lg""","lg,lg" 

TERMINATED BY WRITESPACE:装载这种数据: 10 lg lg 



LOAD DATA  
INFILE *  
INTO TABLE DEPT  
REPLACE  
FIELDS TERMINATED BY WHITESPACE  
--FIELDS TERMINATED BY x'20'  
(DEPTNO,  
 DNAME,  
 LOC  
)
BEGINDATA  
10 Sales Virginia



 3、指定不装载那一列



LOAD DATA  
INFILE *  
INTO TABLE DEPT  
REPLACE  
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'  
(DEPTNO,  
 FILLER_1 FILLER,  --下面的 "Something Not To Be Loaded" 将不会被装载  
 DNAME,  
 LOC  
)  
BEGINDATA  
20,Something Not To Be Loaded,Accounting,"Virginia,USA"



4、position:当没声明FIELDS TERMINATED BY ',' 时 ,用位置告诉字段装载数据

结果:10 | Accounting Vir | ginia,USA | 10Accounting Virginia,USA



LOAD DATA  
INFILE *  
INTO TABLE DEPT  
REPLACE  
(DEPTNO position(1:2),  
 DNAME position(*:16), --这个字段的开始位置在前一字段的结束位置  
 LOC position(*:29),  
 ENTIRE_LINE position(1:29)  
)  
BEGINDATA 
10Accounting Virginia,USA



5、使用函数日期的一种表达TRAILING NULLCOLS的使用

注:可以通过:dname类型调用函数,特别注意date函数的使用。



LOAD DATA  
INFILE *  
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ','
TRAILING NULLCOLS
--这句的意思是将没有对应值的列都置为null
--如果第一行改为 10,Sales,Virginia,1/5/2000,, 就不用TRAILING NULLCOLS了  
(DEPTNO,  
 DNAME "upper(:dname)", --使用函数  
 LOC "upper(:loc)",  
 LAST_UPDATED date 'dd/mm/yyyy', --日期的一种表达方式。还有'dd-mon-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



6、合并多行记录为一行记录

注:例如有些文本文件以N行为一循环记录数据,则可以这样导入。



LOAD DATA  
INFILE *  
concatenate 3 --通过关键字concatenate 把几行的记录看成一行记录  
INTO TABLE DEPT  
replace  
FIELDS TERMINATED BY ','  
(DEPTNO,  
 DNAME "upper(:dname)",  
 LOC "upper(:loc)",  
 LAST_UPDATED date 'dd/mm/yyyy' 
)  
BEGINDATA  
10,Sales,
Virginia,
1/5/2000



7、使用continueif来合并记录行

上例可直接使用continueif last= ','来告诉Oracle如果前一个数据以','结尾,则这个附加到上一行

注:ContinueIf还可以使用this或next选项



LOAD DATA  
INFILE *  
continueif last= ','
INTO TABLE DEPT  
replace  
FIELDS TERMINATED BY ','  
(DEPTNO,  
 DNAME "upper(:dname)",  
 LOC "upper(:loc)",  
 LAST_UPDATED date 'dd/mm/yyyy' 
)  
BEGINDATA  
10,Sales,
Virginia,
1/5/2000



8、载入每行的行号,关键字:RECNUM 



LOAD DATA  
INFILE *  
INTO TABLE DEPT 
replace  
(DEPTNO      RECNUM //载入每行的行号  
 ENTIRE_LINE Position(1:1024)
)  
BEGINDATA  
fsdfasj     --自动分配行号到DEPTNO字段,此行为1  
fasdjfasdfl --自动递增,此行为2



9、载入有换行符的数据

①使用一个非换行符的字符

注:换行的特殊字符如果使用'\n',则会在Windows编译过程中直接换成换行符,导致无法转换



LOAD DATA  
INFILE *  
INTO TABLE DEPT  
REPLACE  
FIELDS TERMINATED BY ','  
TRAILING NULLCOLS  
(DEPTNO,  
DNAME "upper(:dname)",  
LOC "upper(:loc)",  
LAST_UPDATED "my_to_date( :last_updated )",  
COMMENTS "replace(:comments,'%%',chr(10))"  --用replace函数转换成换行符  
)  
BEGINDATA  
10,Sales,Virginia,01-april-2001,This is the Sales%%Office in Virginia  
20,Accounting,Virginia,13/04/2001,This is the Accounting%%Office in Virginia  
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting%%Office in Virginia  
40,Finance,Virginia,987268297,This is the Finance%%Office in Virginia



②使用fix属性: fix告诉每次取一行的范围, 然后,通过看这一行相应的分隔符去分开对应字段中的数据

注:fix只能加在外部文件数据导入时,另外需要每行数据长度都相等。



Load DATA  
INFILE demo1.dat "fix 68"  
INTO TABLE t1_a  
REPLACE  
FIELDS TERMINATED BY ','  
TRAILING NULLCOLS  
(DEPTNO,  
DNAME "upper(:dname)",  
LOC "upper(:loc)",  
LAST_UPDATED Date 'dd/mm/yyyy',
ENTIRE_LINE  
)
-------------------------------------------------
demo1.dat  
----------
10,aaaab,Virginia,01/05/2001,This is the aaaab
Office in Virginia
20,aaaac,Virginia,13/04/2001,This is the aaaac
Office in Virginia
30,aaaad,Virginia,14/04/2001,This is the aaaad
Office in Virginia
40,aaaae,Virginia,16/02/2001,This is the aaaae
Office in Virginia



③ 使用var属性:其实就是每次开头告诉sqlldr每行变化取多少位,相当于每次都指定个fix多少(由前面的数据决定位数)的值。这也就是变长的做法。



Load DATA  
INFILE demo17.dat "var 3"  
INTO TABLE t1_a  
REPLACE  
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS  
(DEPTNO,  
 DNAME "upper(:dname)",  
 LOC "upper(:loc)",  
 LAST_UPDATED Date 'dd/mm/yyyy',
 ENTIRE_LINE  
)
----------------------------------------------
demo17.dat  
----------
03510,Sales,Virginia,01/01/2001,This
03920,Accounting,Virginia,13/04/2001,Thi
04530,Consulting,Virginia,14/04/2001,This is t
07140,Finance,Virginia,14/04/2001,This is the Finance Office
 in Virginia
注:var 3 表示前三位用于说明该条记录的长度 (但是谁告诉我长度怎么数的?-_-|||)



④使用str属性
可使用str来定义一个行结尾符
计算以|\r\n 结束的值:
select utl_raw.cast_to_raw('|'||chr(13)||chr(10)) from dual;
结果:7C0D0A

注意:同样需要在外部文件数据导入中使用,且最后一个不用加;另外注意不要有空格



LOAD DATA  
INFILE demo17.dat "str X'7C0D0A'"  
INTO TABLE t1_a  
REPLACE  
FIELDS TERMINATED BY ','  
TRAILING NULLCOLS  
(DEPTNO,  
 DNAME "upper(:dname)",  
 LOC "upper(:loc)",  
 LAST_UPDATED Date 'dd/mm/yyyy',
 ENTIRE_LINE  
)  
-----------------------------------------------
demo17.dat  
----------
10,Sales,Virginia,01/01/2001,This is the Sales  
Office in Virginia|
20,Accounting,Virginia,13/04/2001,This is the Accounting  
Office in Virginia|
30,Consulting,Virginia,14/04/2001,This is the Consulting  
Office in Virginia|
40,Finance,Virginia,14/04/2002,This is the Finance  
Office in Virginia



10、nullif导入

注:需要注意的是在前面指定的数据类型以及后面的引号!



LOAD DATA
INFILE *
INTO TABLE t1_a
REPLACE
(DEPTNO position(1:2) integer external nullif DEPTNO='1',
 -- 当导入deotno的值为'1'时,则该条记录不导入
 DNAME position(3:8)
)
BEGINDATA
1 10
20lg



 11、使用自定义的函数。以为是解决的时间问题的例子:



create or replace 
my_to_date( p_string in varchar2 ) return date 
as 
type fmtArray is table of varchar2(25); 

l_fmts fmtArray := fmtArray( 'dd-mon-yyyy', 'dd-month-yyyy', 
'dd/mm/yyyy', 
'dd/mm/yyyy hh24:mi:ss' ); 
l_return date; 
begin 
for i in 1 .. l_fmts.count 
loop 
begin 
l_return := to_date( p_string, l_fmts(i) ); 
exception 
when others then null; 
end; 
EXIT when l_return is not null; 
end loop; 

if ( l_return is null ) 
then 
l_return := 
new_time( to_date('01011970','ddmmyyyy') + 1/24/60/60 * 
p_string, 'GMT', 'EST' ); 
end if; 

return l_return; 
end;



LOAD DATA 
INFILE * 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "upper(:dname)", 
LOC "upper(:loc)", 
LAST_UPDATED "my_to_date( :last_updated )" // 使用自定义的函数 
) 
BEGINDATA 
10,Sales,Virginia,01-april-2001 
20,Accounting,Virginia,13/04/2001 
30,Consulting,Virginia,14/04/2001 12:02:02 
40,Finance,Virginia,987268297 
50,Finance,Virginia,02-apr-2001 
60,Finance,Virginia,Not a date