-- Start
SQL*Loader 是 Oracle 提供的一个工具用来将文件中的数据导入到一个或多个表中。你可以在 Oracle 的安装目录中找到它 C:\oraclexe\app\oracle\product\11.2.0\server\bin\sqlldr.exe
目前,我们最常用的用来传输数据的文件格式有 2 种,一种是分隔符文件(如:CSV),另一种是定长文件,下面我们通过例子来看看如果导入这两种文件。
首先,我们创建如下的表。
CREATE TABLE EMPLOYEES ( EMPLOYEE_ID NUMBER(6,0) NOT NULL, FIRST_NAME VARCHAR2(20) NOT NULL, LAST_NAME VARCHAR2(25) NOT NULL, EMAIL VARCHAR2(50) NULL, HIRE_DATE DATE NULL, SALARY NUMBER(8,2) NULL, UPDATE_DATE_TIME TIMESTAMP NULL );
假设我们有下面的文件(C:\log\employees.txt)
1,bo,shang,2015-01-01,8888.88,2015-02-19 22:15:00.000 2,san,zhang,2015-02-02,9999.99,2015-02-19 22:15:00.000
sqlldr USERID=DBuser/shangbo -- 指定连接数据库用户名,密码 ROWS=1000 -- 每 1000 条提交一次 BAD=C:\log\employees.bad -- 保存那些在导入的过程中,由于格式等问题出错的行 DISCARD=C:\log\employees.dsc -- 保存那些由于某些条件不满足而丢弃的行 LOG=C:\log\employees.log -- 保存日志文件 CONTROL=C:\log\employees.ctl -- 控制文件,用来告诉 SQL*Loader 如何导入数据
-- 要导入的数据文件 LOAD DATA INFILE 'C:\log\employees.txt' -- APPEND 表示将数据添加到表中 -- REPLACE 表示先 DELETE 所有数据,然后导入 -- TRUNCATE 表示先 TRUNCATE 所有数据,然后导入 -- INSERT 只适用于空表,如不是空表会报错 APPEND INTO TABLE employees FIELDS TERMINATED BY ',' -- 表示文件中字段之间使用逗号分隔 ( EMPLOYEE_ID CHAR, -- 此处的字段名必须和表的列名一致 FIRST_NAME , -- 如没有指定数据类型,默认为 CHAR LAST_NAME , -- 注意,此处的数据类型是 SQL*Loader 的数据类型,和表的数据类型不一样 HIRE_DATE DATE "YYYY-MM-DD", -- 指定 DATE 类型和格式 SALARY CHAR, -- SQL*Loader 的 CHAR 类型可以自动转成任何表的数据类型 UPDATE_DATE_TIME TIMESTAMP "YYYY-MM-DD HH24:MI:SS.FF3" )
有些分隔符文件的字段是用双引号括起来的,还有些字段的前面或后面有空格,如何处理这样的文件呢?
1,"bo","shang","2015-01-01",8888.88,"2015-02-19 20:15:00.000" 2,"san","zhang","2015-02-02",9999.99,"2015-02-18 21:15:00.000" 3 ,"si" ,"li" ,"2015-03-01" ,6666.66 ,"2015-02-17 22:15:00.000" 4, "wu", "wang", "2015-04-02", 7777.77, "2015-02-16 23:15:00.000"
LOAD DATA INFILE 'C:\log\employees.txt' REPLACE INTO TABLE employees FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ( EMPLOYEE_ID , FIRST_NAME , LAST_NAME , HIRE_DATE DATE "YYYY-MM-DD", SALARY , UPDATE_DATE_TIME TIMESTAMP "YYYY-MM-DD HH24:MI:SS.FF3" )
上面两个文件都是比较完美的,有时候实际的情况是非常复杂的,例如,现在要求你把下面的文件导入到下面的表中。要求
1. 忽略第一行和最后一行
2. EMPLOYEE_ID 由数据库自动生成
3. NAME 由 FIRST_NAME 和 LAST_NAME 组成且要转成大写,如:SHANGBO
4. 忽略 ENGLISH_NAME 列
5. 如果 HIRE_DATE 是空,则插入 NULL
6. 如果 SALARY 是空,则插入 0
7. ENTER_USER 插入默认值 'SYSTEM'
8. ENTER_DATE_TIME 插入当前系统时间
9. 忽略 ACTIVE 是 n 的行
要导入的文件
FIRST_NAME,LAST_NAME,ENGLISH_NAME,HIRE_DATE,SALARY,ACTIVE bo,shang,scott,2015-01-01,8888.88,y san,zhang,mike,,,y si ,li ,darren , , ,y wu, wang, eric, 2015-04-02, 7777.77, n END OF FILE
要导入的表
CREATE TABLE EMPLOYEES ( EMPLOYEE_ID NUMBER(6,0) NOT NULL, NAME VARCHAR2(20 BYTE) NOT NULL, HIRE_DATE DATE NULL, SALARY NUMBER(8,2) DEFAULT 0 NOT NULL, ENTER_USER VARCHAR2(20 BYTE) NULL, ENTER_DATE_TIME TIMESTAMP(6) NULL );
控制文件
LOAD DATA INFILE 'C:\log\employees.txt' REPLACE INTO TABLE employees WHEN (ACTIVE!='n') FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ( EMPLOYEE_ID SEQUENCE(1, 1), FIRST_NAME BOUNDFILLER, -- BOUNDFILLER 表示该列不保存在表中,但可以用在下面的 EXPRESSION 表达式中 LAST_NAME BOUNDFILLER, NAME EXPRESSION "UPPER(RTRIM(:LAST_NAME) || RTRIM(:FIRST_NAME))", -- EXPRESSION 表示后面是一个SQL表达式 ENGLISH_NAME FILLER, -- FILLER 表示该列不保存在表中,它和 BOUNDFILLER 区别是它不能用在 EXPRESSION 表达式中 HIRE_DATE DATE "YYYY-MM-DD" NULLIF HIRE_DATE=BLANKS, -- NULLIF 表示如果文件中 HIRE_DATE 是一个或多个空白字符,则插入 NULL 到表中 SALARY "NVL(:SALARY, 0)", -- :SALARY 表示引用文件中的 SALARY 列 ACTIVE FILLER, -- 忽略此列 ENTER_USER CONSTANT 'SYSTEM', -- CONSTANT 表示后面是一个常量 ENTER_DATE_TIME EXPRESSION "CURRENT_TIMESTAMP" )
-- SKIP=1 表示跳过第一条 sqlldr USERID=DBuser/shangbo ROWS=1000 BAD=C:\log\employees.bad DISCARD=C:\log\employees.dsc LOG=C:\log\employees.log CONTROL=C:\log\employees.ctl SKIP=1
现在我们把“分隔符文件例子3”中的文件改成定长文件,看看如何导入定长文件。
要导入的文件
FIRST_NAME LAST_NAME ENGLISH_NAME HIRE_DATE SALARY ACTIVE bo shang scott 2015-01-01 8888.88 y san zhang mike y si li darren y wu wang eric 2015-04-02 7777.77 n END OF FILE
CREATE TABLE EMPLOYEES ( EMPLOYEE_ID NUMBER(6,0) NOT NULL, NAME VARCHAR2(20 BYTE) NOT NULL, HIRE_DATE DATE NULL, SALARY NUMBER(8,2) DEFAULT 0 NOT NULL, ENTER_USER VARCHAR2(20 BYTE) NULL, ENTER_DATE_TIME TIMESTAMP(6) NULL );
-- fix 表示它是一个定长文件,62表示每行的长度,注意包含换行符哦 LOAD DATA INFILE 'C:\log\employees.txt' "fix 62" REPLACE INTO TABLE employees WHEN (ACTIVE='y') ( EMPLOYEE_ID SEQUENCE(1, 1), FIRST_NAME BOUNDFILLER POSITION(1:11), -- 位置从1开始 LAST_NAME BOUNDFILLER POSITION(12-21), -- : 和 - 完全相同 NAME EXPRESSION "UPPER(:LAST_NAME || :FIRST_NAME)", ENGLISH_NAME FILLER POSITION(*:34), -- * 表示相对上一个位置开始 HIRE_DATE POSITION(*:45) DATE "YYYY-MM-DD" NULLIF HIRE_DATE=BLANKS, SALARY POSITION(*:54) "NVL(:SALARY, 0)", ACTIVE FILLER POSITION(*), ENTER_USER CONSTANT 'SYSTEM', ENTER_DATE_TIME EXPRESSION "CURRENT_TIMESTAMP" )
上面例子的导入方法本质上是构造 INSERT 语句,Oracle 还提供了一种直接路径导入的方法,它是将要导入的文件直接格式化成数据库文件,所以速度会更快。
在使用直接路径导入之前,我们需要运行下面的 SQL 设置一下数据库,这个 SQL 只需要设置一次。
@C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\catldr.sql
下面的命令使用直接路径导入“分隔符文件例子1”的文件
-- DIRECT=true 设置直接路径导入 -- PARALLEL=true 使用并行导入 sqlldr USERID=DBuser/shangbo DIRECT=true PARALLEL=true ROWS=10000 BAD=C:\log\employees.bad DISCARD=C:\log\employees.dsc LOG=C:\log\employees.log CONTROL=C:\log\employees.ctl
-- 更多参见:Oracle 精萃
-- 声明:转载请注明出处
-- Last edited on 2015-02-21
-- Created by ShangBo on 2015-02-20
-- End