陈老师:1415968548 郑老师:2735197625 乐老师:354331153
客服热线:
19941464235 / 19906632509 / 19906733890 / 19905812933(微信同号)

客服微信

【Oracle OCP】Oracle 19c之SQL LOADER

作者:炎燚小寶
发布时间:2023-12-19 09:22
浏览量:922

本文为云贝教育 刘峰 原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、演绎和未经注明出处的转载。

一、SQL*Loader的定义及用途

SQL*Loader 是一个批量加载实用程序,用于将数据从外部文件移动到 Oracle 数据库中。 SQL*Loader支持多种加载格式、选择性加载、多表加载。

SQL*Loader (sqlldr) 是用于高性能数据加载的实用程序。数据可以从任何文本文件加载并插入到数据库中。


二、如何使用SQL*Loader

可以使用 sqlldr(在某些平台上为 sqlload)实用程序将数据加载到 Oracle 数据库中。调用不带参数的实用程序以获取可用参数的列表。看下面的例子:

sqlldr username/password@server control=loader.ctl


此示例控制文件 (loader.ctl) 将加载包含分隔数据的外部数据文件:

load data
 infile '/home/oracle/sqlloader/mydata.csv'
 into table emp
 fields terminated by "," optionally enclosed by '"'
 ( empno, empname, sal, deptno )
参数说明:
INTO TABLE emp             --要插入记录的表
Fields terminated by ","      --数据中每行记录用 "," 分隔
optionally enclosed by '"' --数据中每个字段用 '"' 框起,导入的时间不会将"作为数据导入表字段中。比如empname字段中存储Scott Tiger和"Scott Tiger"的区别


mydata.csv 文件如下所示:

10001,"Scott Tiger", 1000, 40
10002,"Frank Naude", 500, 20


创建emp表语句如下:

create table emp(empno number,empname varchar2(100),sal number,deptno int);


导入成功的输出日志如下:

Path used:      Conventional
Commit point reached - logical record count 2
Table EMP:
  2 Rows successfully loaded.
Check the log file:
  loader.log
for more information about the load.


或者,您可以使用以下语法之一来处理制表分隔文件:

fields terminated by "\t"
fields terminated by X'09'


此外,如果您的文件采用 Unicode,您可以进行以下添加。

load data
 CHARACTERSET UTF8
 infile '/home/oracle/sqlloader/mydata.csv'
 into table emp
 fields terminated by "," optionally enclosed by '"'
 ( empno, empname, sal, deptno )


另一个示例控制文件,其中内联数据格式化为固定长度记录。诀窍是指定“*”作为数据文件的名称,并使用 BEGINDATA 来启动控制文件中的数据部分:

load data
 infile *
 replace
 into table departments
 (  dept     position (02:05) char(4),
    deptname position (08:27) char(20)
 )
 
begindata
COSC  COMPUTER SCIENCE
ENGL  ENGLISH LITERATURE
MATH  MATHEMATICS
POLY  POLITICAL SCIENCE


导入成功之后的数据如下

DEPT                                     DEPTNAME
---------------------------------------- ----------------------------------------
OSC                                      OMPUTER SCIENCE
NGL                                      NGLISH LITERATURE
ATH                                      ATHEMATICS
OLY                                      OLITICAL SCIENCE


三、如何加载Excel数据到Oracle?

打开 MS-Excel 电子表格并将其另存为 CSV(逗号分隔值)文件。现在可以将该文件复制到 Oracle 计算机并使用 SQL*Loader 实用程序加载。

可能出现的问题和解决方法:

电子表格可能包含带有换行符 (ALT+ENTER) 的单元格。 SQL*Loader 期望整个记录位于一行上。运行以下宏来删除换行符(工具 - 宏 - Visual Basic 编辑器):

Removing tabs and carriage returns from worksheet cells
Sub CleanUp()
 Dim TheCell As Range
 On Error Resume Next
 For Each TheCell In ActiveSheet.UsedRange
   With TheCell
     If .HasFormula = False Then
       .Value = Application.WorksheetFunction.Clean(.Value)
     End If
   End With
 Next TheCell
End Sub


四、SQL*LOADER程序如何下载数据到平面文件?

Oracle 不提供任何数据卸载实用程序。以下是一些解决方法:


4.1、使用SQL*Plus

您可以使用 SQL*Plus 选择数据并格式化数据,然后将其假脱机到文件中。此示例假脱机输出可导入 MS-Excel 的 CSV(逗号分隔值)文件:

set echo off newpage 0 space 0 pagesize 0 feed off head off trimspool on
spool oradata.txt
select col1 || ',' || col2 || ',' || col3
 from tab1
 where col2 = 'XYZ';
spool off


警告:如果您的数据包含逗号,请选择数据中没有的另一个分隔符。您还可以将包含逗号的列括起来。


如果您不想手动输入逗号,也可以使用“set colsep”命令。这可以节省大量的打字工作。例子:

set colsep ','
set echo off newpage 0 space 0 pagesize 0 feed off head off trimspool on
spool oradata1.txt
select col1, col2, col3
  from tab1
 where col2 = 'XYZ';
spool off


4.2、使用PL/SQL

PL/SQL的UTL_FILE包也可以用来卸载数据。例子:

DECLARE
    HANDLE UTL_FILE.FILE_TYPE;
BEGIN
    HANDLE := UTL_FILE.FOPEN('HR_OUT','EMP.TXT','W',1000);
    FOR I IN (SELECT EMPNO|| ',' || EMPNAME|| ',' || SAL||','|| DEPTNO as MSG FROM HR.EMP T) LOOP
        UTL_FILE.PUT_LINE(HANDLE,I.MSG);
    END LOOP;
    UTL_FILE.FFLUSH(HANDLE);
    UTL_FILE.FCLOSE(HANDLE);
END;
/


五、加载可变和固定长度的数据


5.1、加载可变长度的数据

在第一个示例中,我们将展示如何将分隔(可变长度)数据加载到 Oracle 中:

LOAD DATA
 INFILE '/home/oracle/sqlloader/mydata1.csv'
 INTO TABLE load_delimited_data
 FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY '"'
 TRAILING NULLCOLS
 (  data1,
    data2
 )
trailing nullcols                   --表的字段没有对应的值时允许为空
 
mydata1.csv
11111,AAAAAAAAAA
22222,"A,B,C,D,"


示例

create table load_delimited_data(data1 number,data2 varchar2(50));
sqlldr hr/hr@pdb2 control=load5.ctl


注意:SQL*Loader 中的默认数据类型是 CHAR(255)。要加载长度超过 255 个字符的字符字段,请在控制文件中对类型和长度进行编码。通过这样做,Oracle 将分配一个足够大的缓冲区来容纳整个列,从而消除潜在的“数据文件中的字段超出最大长度”错误。例子:

...
resume char(4000),
...


5.2、加载固定长度的数据

如果需要加载位置数据(固定长度),请查看以下控制文件示例:

LOAD DATA
  INFILE '/home/oracle/sqlloader/mydata2.csv'
  INTO TABLE load_positional_data
  (  data1 POSITION(1:5),
     data2 POSITION(6:15)
  )
  
mydata2.csv
11111AAAAAAAAAA
22222BBBBBBBBBB


例如,position(01:05) 将给出第 1 到第 5 个字符(11111 和 22222)。

示例

create table load_positional_data(data1 number,data2 varchar2(50));
sqlldr hr/hr@pdb2 control=load6.ctl


通过指定“SKIP=n”关键字,可以跳过不需要的标头记录或继续中断的加载(例如,如果空间不足)。 “n”指定要跳过的逻辑行数。看看这些例子:

OPTIONS ( SKIP=3)
LOAD DATA
INFILE '/home/oracle/sqlloader/mydata3.csv'
INTO TABLE load_positional_data
(  data1 POSITION(1:5),
   data2 POSITION(6:15)
)
mydata3.csv
11111AAAAAAAAAA
22222BBBBBBBBBB
33333CCCCCCCCCC
44444DDDDDDDDDD
55555EEEEEEEEEE
66666FFFFFFFFFF
 
sqlldr userid=ora_id/ora_passwd control=control_file_name.ctl  skip=3
加载成功的数据
44444DDDDDDDDDD
55555EEEEEEEEEE
66666FFFFFFFFFF


如果要继续进行多表直接路径加载,则可能需要使用 CONTINUE_LOAD 子句而不是 SKIP 参数。 CONTINUE_LOAD 允许您为正在加载的每个表指定要跳过的不同行数。


六、加载过程中加工处理

数据加载到 Oracle 数据库时可以进行修改。还可以使用静态值或派生值填充列。但是,这仅适用于常规加载路径(不适用于直接路径加载)。这里有些例子:

LOAD DATA
  INFILE '/home/oracle/sqlloader/mydata4.csv'
  INTO TABLE modified_data
  (  rec_no                      "my_db_sequence.nextval",
     region                      CONSTANT '31',
     time_loaded                 "to_char(SYSDATE, 'HH24:MI')",
     data1        POSITION(1:5)  ":data1/100",
     data2        POSITION(6:15) "upper(:data2)",
     data3        POSITION(16:22)"to_date(:data3, 'YYMMDD')"
  )
  
mydata4.csv
11111AAAAAAAAAA991201
22222BBBBBBBBBB990112


示例

create table modified_data(rec_no number,region varchar2(10),time_loaded date,data1 number,data2 varchar2(20),data3 date);
CREATE SEQUENCE my_db_sequence
INCREMENT BY 1
START WITH 1
MINVALUE 0
NOCYCLE
CACHE 20
sqlldr hr/hr@pdb2 control=load8.ctl
 select * from modified_data;
    REC_NO REGION     TIME_LOADED               DATA1 DATA2                DATA3
---------- ---------- -------------------- ---------- -------------------- -------------------
         4 31         16:58                    111.11 AAAAAAAAAA           2099-12-01 12:00:00
         5 31         16:58                    222.22 BBBBBBBBBB           2099-01-12 12:00:00


坏文件记录不符合要求的数据

LOAD DATA
  INFILE '/home/oracle/sqlloader/mydata4.csv'
  BADFILE '/home/oracle/sqlloader/mydata4.bad'
  DISCARDFILE '/home/oracle/sqlloader/mydata4.dis'
  APPEND
  INTO TABLE modified_data
  (  rec_no                      "my_db_sequence.nextval",
     region                      CONSTANT '31',
     time_loaded                 "to_char(SYSDATE, 'HH24:MI')",
     data1        POSITION(1:5)  ":data1/100",
     data2        POSITION(6:15) "upper(:data2)",
     data3        POSITION(16:22)"to_date(:data3, 'YYMMDD')"
  )
  
  
BADFILE 被SQL*Loader拒绝的记录,被拒绝的记录可能是不符合要求的记录。
DISCARDFILE 记录没有通过的数据。
APPEND  追加数据
比如
mydata4.csv
11111AAAAAAAAAA991201
22222BBBBBBBBBB990112
33333CCCCCCCCCC998711-->这行数据将被记录到badfile里


6.1、从多个文件加载数据

可以通过重复 INFILE 子句从多个输入文件加载,前提是它们使用相同的记录格式。这是一个例子:

LOAD DATA
  INFILE file1.dat
  INFILE file2.dat
  INFILE file3.dat
  APPEND
  INTO TABLE emp
  ( EMPNO  POSITION(1:4)   INTEGER EXTERNAL,
    EMPNAME  POSITION(6:15)  CHAR,
    SAL    POSITION(17:18) CHAR,
    DEPTNO    POSITION(20:23) INTEGER EXTERNAL
  )


示例

file1.dat
1001AAAAAAAAAAA39403
file2.dat
1002BBBBBBBBBBB40506
file3.dat
1003CCCCCCCCCCC50708
sqlldr hr/hr@pdb2 control=load9.ctl


6.2、加载数据到多个表中

还可以在 SQL*Loader 控制文件中指定多个“INTO TABLE”子句来加载到多个表中。看下面的例子:“选项卡”字段被标记为 FILLER,因为我们不想加载它。

LOAD DATA
 INFILE '/home/oracle/sqlloader/mydata5.csv'
 INTO TABLE tab1 WHEN tab = 'tab1'
   ( tab  FILLER CHAR(4),
     col1 INTEGER
   )
 INTO TABLE tab2 WHEN tab = 'tab2'
   ( tab  FILLER POSITION(1:4),
     col1 INTEGER
   )
   
mydata5.csv
tab1|1
tab1|2
tab2|2
tab3|3



示例

create table tab1(tab varchar2(20),col1 int);
create table tab2(tab varchar2(20),col1 int);
sqlldr hr/hr@pdb2 control=load10.ctl


“选项卡”字段被标记为 FILLER,因为我们不想加载它。

请注意在第二个路由值上使用“POSITION”(tab = 'tab2')。默认情况下,对于新的 INTO TABLE 子句,字段扫描不会从记录的开头重新开始。相反,扫描会从中断处继续。需要 POSITION 才能将指针重新重置到记录的开头。在分隔格式中,在第一列后使用“POSITION(1)”来重置指针。


另一个例子:

LOAD DATA
 INFILE 'mydata.dat'
 REPLACE
 INTO TABLE emp
      WHEN empno != ' '
 ( empno  POSITION(1:4)   INTEGER EXTERNAL,
   ename  POSITION(6:15)  CHAR,
   deptno POSITION(17:18) CHAR,
   mgr    POSITION(20:23) INTEGER EXTERNAL
 )
 INTO TABLE proj
      WHEN projno != ' '
 (  projno POSITION(25:27) INTEGER EXTERNAL,
    empno  POSITION(1:4)   INTEGER EXTERNAL
 )



是否可以有选择地仅加载需要的记录?

LOAD DATA
  INFILE  'mydata.dat' BADFILE  'mydata.bad' DISCARDFILE 'mydata.dis'
  APPEND
  INTO TABLE my_selective_table
  WHEN (01) <> 'H' and (01) <> 'T' and (30:37) = '20031217'
  (
     region              CONSTANT '31',
     service_key         POSITION(01:11)   INTEGER EXTERNAL,
     call_b_no           POSITION(12:29)   CHAR
  )



注意:SQL*Loader 不允许在 WHEN 子句中使用 OR。您只能使用 AND,如上例所示!要解决此问题,请编写多个“INTO TABLE ... WHEN”子句。这是一个例子:

LOAD DATA
  INFILE  'mydata.dat' BADFILE  'mydata.bad' DISCARDFILE 'mydata.dis'
  APPEND
  INTO TABLE my_selective_table
  WHEN (01) <> 'H' and (01) <> 'T'
  (
     region              CONSTANT '31',
     service_key         POSITION(01:11)   INTEGER EXTERNAL,
     call_b_no           POSITION(12:29)   CHAR
  )
  INTO TABLE my_selective_table
  WHEN (30:37) = '20031217'
  (
     region              CONSTANT '31',
     service_key         POSITION(01:11)   INTEGER EXTERNAL,
     call_b_no           POSITION(12:29)   CHAR
  )



七、跳过数据加载

不能将 POSITION(x:y) 与分隔数据一起使用。幸运的是,从 Oracle 8i 开始,我们可以指定 FILLER 列。 FILLER 列用于跳过加载文件中的列/字段,忽略不需要的字段。看这个例子:

LOAD DATA
TRUNCATE
INTO TABLE T1
FIELDS TERMINATED BY ','
( field1,
  field2 FILLER,
  field3
)



如果稍后再次需要跳过的列的值,则可以使用 BOUNDFILLER(适用于 Oracle 9i 及更高版本)。这是一个例子:

LOAD DATA
INFILE '/home/oracle/sqlloader/mydata6.csv'
TRUNCATE
INTO TABLE t1
FIELDS TERMINATED BY ","  trailing nullcols
(
 c1,
 field2 BOUNDFILLER,
 field3 BOUNDFILLER,
 field4 BOUNDFILLER,
 field5 BOUNDFILLER,
 c2     ":field2 || :field3",
 c3     ":field4 + :field5"
)



示例

create table t1(c1 varchar2(100),c2 varchar2(100),c3 varchar2(100));
mydata6.csv
aa,bb,cc,11,22,ff,gg
sqlldr hr/hr@pdb2 control=load11.ctl



八、多行加载为一行

可以使用以下两个子句之一从多个物理记录创建一个逻辑记录:
CONCATENATE - 当 SQL*Loader 应该将相同数量的物理记录组合在一起形成一个逻辑记录时使用它。

示例

#创建测试表
create table multi_line(
x1 varchar2(100),x2 varchar2(100),
x3 varchar2(100),x4 varchar2(100),
x5 varchar2(100),x6 varchar2(100));

#准备测试数据
mydate12.csv
a1,b1,c1,
a2,b2,c2,
a3,b3,c3,
a4,b4,c4,
a5,b5,c5,
a6,b6,c6,
a7,b7,c7,
a8,b8,c8,
a9,b9,c9,
    
#编写控制文件
load12.ctl
LOAD DATA
INFILE 'mydate12.csv'
concatenate 2
INTO TABLE multi_line
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
( x1,
  x2,
  x3,
  x4,
  x5,
  x6
)

#导入
sqlldr hr/hr@pdb2 control=load12.ctl

#验证
set linesize 100
set pagesize 0
col x1 for a10
col x2 for a10
col x3 for a10
col x4 for a10
col x5 for a10
col x6 for a10
select * from multi_line;
a1         b1         c1         a2         b2         c2
a3         b3         c3         a4         b4         c4
a5         b5         c5         a6         b6         c6
a7         b7         c7         a8         b8         c8
a9         b9         c9



CONTINUEIF - 如果条件指示应将多条记录视为一条记录,则使用。例如。在第 1 列中添加“#”字符。

#编写控制文件
load13.ctl
LOAD DATA
INFILE 'mydate13.csv'
continueif next (1:2) = '--'
INTO TABLE multi_line
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
( x1,
  x2,
  x3,
  x4,
  x5,
  x6
)
#准备数据
mydata13.csv
a1,b1,c1,
--a2,b2,c2,
a3,b3,c3,
--a4,b4,c4,
a5,b5,c5,
a6,b6,c6,
--a7,b7,c7,
a8,b8,c8,
a9,b9,c9,
    
#导入
sqlldr hr/hr@pdb2 control=load13.ctl
    
#验证
set linesize 100
set pagesize 0
col x1 for a10
col x2 for a10
col x3 for a10
col x4 for a10
col x5 for a10
col x6 for a10
select * from multi_line;
           b1         c1         a2         b2         c2
           b3         c3         a4         b4         c4
           b5         c5
           b6         c6         a7         b7         c7
           b8         c8
           b9         c9
#这里X1是空值,因为过滤条件要第一个字符是--



九、如何加载记录与多行字段?

使用流记录格式,您可以定义记录分隔符,以便允许您在字段内容中使用默认分隔符('
')。
在 INFILE 子句之后设置分隔符:

 load data
 infile "test.dat"
  "str '|\n'"
 into test_table
 fields terminated by ';' TRAILING NULLCOLS
 (
   desc,
   txt
 )


这里有|和换行符作为字段隔离判断


test.dat:

one line;hello dear world;|
 two lines;Dear world,
 hello!;|


示例

select * from test_table;
DESC1                          TXT
------------------------------ --------------------------------------------------
one line                       hello dear world
 two lines                     Dear world,
                                hello!


请注意,这似乎不适用于内联数据(INFILE * 和 BEGINDATA)。


十、SQL*Loader Express

SQL*Loader Express 实际上只是命令行界面的简单扩展,因此所有默认值都旨在处理与表结构匹配的基本 CSV 文件。例如,如果我们要将数据加载到 EMP2 表中,默认设置将查找名为“EMP2.dat”的转储文件,该文件内部与 EMP2 表的结构匹配。因此,如果派生设置和默认设置不可接受,我们只需指定命令行参数。


注意事项

不能,但通过将 ROWS= 参数设置为较大的值,可以减少提交。当您使用较高的 ROWS= 值时,请确保准备好大的回滚段。

rows -- 常规路径绑定数组中或直接路径保存数据间的行数



十二、提高 SQL*Loader 的性能

传统的路径加载器本质上是使用标准 INSERT 语句加载数据。直接路径加载器 (DIRECT=TRUE) 绕过与之相关的大部分逻辑,并直接加载到 Oracle 数据文件中。有关直接路径加载限制的更多信息可以从 Oracle Server Utilities Guide 获取。
直接路径加载的一些限制是:


十四、如何使用 SQL*Loader 加载图像、声音片段和文档?

SQL*Loader 可以从“主数据文件”、SDF(辅助数据文件 - 用于加载嵌套表和 VARRAY)或 LOBFILE 加载数据。 LOBFILE 方法提供了一种将文档、照片、图像和音频剪辑加载到 BLOB 和 CLOB 列中的简单方法。看这个例子:

给出下表:

CREATE TABLE image_table (
       image_id   NUMBER(5),
       file_name  VARCHAR2(30),
       image_data BLOB);


控制文件:

LOAD DATA
INFILE *
INTO TABLE image_table
REPLACE
FIELDS TERMINATED BY ','
(
 image_id   INTEGER(5),
 file_name  CHAR(30),
 image_data LOBFILE (file_name) TERMINATED BY EOF
)
BEGINDATA
001,image1.gif
002,image2.jpg
003,image3.jpg