在 oracle 数据库中,我们通常在不同数据库的表间记录进行复制或迁移时会用以下几种方法:
1. a 表的记录导出为一条条分号隔开的 insert 语句,然后执行插入到 b 表中
3. exp a 表,再 imp 到 b 表,exp 时可加查询条件
4. 程序实现 select from a ..,然后 insert into b ...,也要分批提交
5. 再就是本篇要说到的 sql loader(sqlldr) 来导入数据,效果比起逐条 insert 来很明显
第 1 种方法在记录多时是个噩梦,需三五百条的分批提交,否则客户端会死掉,而且导入过程很慢。如果要不产生 redo 来提高 insert into 的性能,就要下面那样做:
<a href="http://writeblog.csdn.net/#viewsource">view source</a>
<code>1.</code><code>alter</code> <code>table</code> <code>b nologging;</code>
<code>2.</code><code>insert</code> <code>/* +append */</code><code>into</code> <code>b(c1,c2)</code><code>values</code><code>(x,xx);</code>
<code>3.</code><code>insert</code> <code>/* +append */</code><code>into</code> <code>b</code><code>select</code> <code>*</code><code>from</code> <code>a@dblink</code><code>where</code> <code>.....;</code>
好啦,前面简述了 oracle 中数据导入导出的各种方法,我想一定还有更高明的。下面重点讲讲 oracle 的 sql loader (sqlldr) 的用法。
在命令行下执行 oracle 的 sqlldr 命令,可以看到它的详细参数说明,要着重关注以下几个参数:
userid -- oracle 的 username/password[@servicename]
control -- 控制文件,可能包含表的数据
-------------------------------------------------------------------------------------------------------
log -- 记录导入时的日志文件,默认为 控制文件(去除扩展名).log
bad -- 坏数据文件,默认为 控制文件(去除扩展名).bad
data -- 数据文件,一般在控制文件中指定。用参数控制文件中不指定数据文件更适于自动操作
errors -- 允许的错误记录数,可以用他来控制一条记录都不能错
rows -- 多少条记录提交一次,默认为 64
skip -- 跳过的行数,比如导出的数据文件前面几行是表头或其他描述
用例子来演示 sqlldr 的使用,有两种使用方法:
1. 只使用一个控制文件,在这个控制文件中包含数据
2. 使用一个控制文件(作为模板) 和一个数据文件
首先,假定有这么一个表 users,并插入五条记录:
<code>1.</code><code>create</code> <code>table</code> <code>users(</code>
<code>2.</code><code> </code><code>user_id number, </code><code>--用户 id</code>
<code>3.</code><code> </code><code>user_name varchar2(50), </code><code>--用户名</code>
<code>4.</code><code> </code><code>login_times number, </code><code>--登陆次数</code>
<code>5.</code><code> </code><code>last_login</code><code>date</code> <code>--最后登录日期</code>
<code>6.</code><code>)</code>
<code>1.</code><code>insert</code> <code>into</code> <code>users</code><code>values</code><code>(1,</code><code>'unmi'</code><code>,3,sysdate);</code>
<code>2.</code><code>insert</code> <code>into</code> <code>users</code><code>values</code><code>(2,</code><code>null</code><code>,5,to_date(</code><code>'2008-10-15'</code><code>,</code><code>'yyyy-mm-dd'</code><code>));</code>
<code>3.</code><code>insert</code> <code>into</code> <code>users</code><code>values</code><code>(3,</code><code>'隔叶黄莺'</code><code>,8,to_date(</code><code>'2009-01-02'</code><code>,</code><code>'yyyy-mm-dd'</code><code>));</code>
<code>4.</code><code>insert</code> <code>into</code> <code>users</code><code>values</code><code>(4,</code><code>'kypfos'</code><code>,</code><code>null</code><code>,</code><code>null</code><code>);</code>
<code>5.</code><code>insert</code> <code>into</code> <code>users</code><code>values</code><code>(5,</code><code>'不知秋'</code><code>,1,to_date(</code><code>'2008-12-23'</code><code>,</code><code>'yyyy-mm-dd'</code><code>));</code>
第二种方式: 使用一个控制文件(作为模板) 和一个数据文件
1) 建立数据文件,我们这里用 pl/sql developer 导出表 users 的记录为 users_data.csv 文件,内容如下:
<code>1.</code><code>" "</code><code>,</code><code>"user_id"</code><code>,</code><code>"user_name"</code><code>,</code><code>"login_times"</code><code>,</code><code>"last_login"</code>
<code>2.</code><code>"1"</code><code>,</code><code>"1"</code><code>,</code><code>"unmi"</code><code>,</code><code>"3"</code><code>,</code><code>"2009-1-5 20:34:44"</code>
<code>3.</code><code>"2"</code><code>,</code><code>"2"</code><code>,</code><code>""</code><code>,</code><code>"5"</code><code>,</code><code>"2008-10-15"</code>
<code>4.</code><code>"3"</code><code>,</code><code>"3"</code><code>,</code><code>"隔叶黄莺"</code><code>,</code><code>"8"</code><code>,</code><code>"2009-1-2"</code>
<code>5.</code><code>"4"</code><code>,</code><code>"4"</code><code>,</code><code>"kypfos"</code><code>,</code><code>""</code><code>,</code><code>""</code>
<code>6.</code><code>"5"</code><code>,</code><code>"5"</code><code>,</code><code>"不知秋"</code><code>,</code><code>"1"</code><code>,</code><code>"2008-12-23"</code>
2) 建立一个控制文件 users.ctl,内容如下:
<code>01.</code><code>options (skip=1,</code><code>rows</code><code>=128)</code><code>-- sqlldr 命令显示的选项可以写到这里边来,skip=1 用来跳过数据中的第一行</code>
<code>02.</code><code>load</code> <code>data</code>
<code>03.</code><code>infile</code><code>"users_data.csv"</code> <code>--指定外部数据文件,可以写多个 infile "another_data_file.csv" 指定多个数据文件</code>
<code>04.</code><code>--这里还可以使用 badfile、discardfile 来指定坏数据和丢弃数据的文件,</code>
<code>05.</code><code>truncate</code> <code>--操作类型,用 truncate table 来清除表中原有记录</code>
<code>06.</code><code>into</code> <code>table</code> <code>users</code><code>-- 要插入记录的表</code>
<code>07.</code><code>fields terminated</code><code>by</code> <code>","</code> <code>-- 数据中每行记录用 "," 分隔</code>
<code>08.</code><code>optionally enclosed</code><code>by</code> <code>'"'</code> <code>-- 数据中每个字段用 '"' 框起,比如字段中有 "," 分隔符时</code>
<code>09.</code><code>trailing nullcols</code><code>--表的字段没有对应的值时允许为空</code>
<code>10.</code><code>(</code>
<code>11.</code><code> </code><code>virtual_column filler,</code><code>--这是一个虚拟字段,用来跳过由 pl/sql developer 生成的第一列序号</code>
<code>12.</code><code> </code><code>user_id number,</code><code>--字段可以指定类型,否则认为是 character 类型, log 文件中有显示</code>
<code>13.</code><code> </code><code>user_name,</code>
<code>14.</code><code> </code><code>login_times,</code>
<code>15.</code><code> </code><code>last_login</code><code>date</code> <code>"yyyy-mm-dd hh24:mi:ss"</code> <code>-- 指定接受日期的格式,相当用 to_date() 函数转换</code>
<code>16.</code><code>)</code>
说明:在操作类型 truncate 位置可用以下中的一值:
1) insert --为缺省方式,在数据装载开始时要求表为空
2) append --在表中追加新记录
3) replace --删除旧记录(用 delete from table 语句),替换成新装载的记录
4) truncate --删除旧记录(用 truncate table 语句),替换成新装载的记录
3) 执行命令:
在 dbservice 指示的数据库的表 users 中记录就和数据文件中的一样了。
执行完 sqlldr 后希望能留意一下生成的几个文件,如 users.log 日志文件、users.bad 坏数据文件等。特别是要看看日志文件,从中可让你更好的理解 sql loader,里面有对控制文件的解析、列出每个字段的类型、加载记录的统计、出错原因等信息。
第一种方式,只使用一个控制文件在这个控制文件中包含数据
1) 把 users_data.cvs 中的内容补到 users.ctl 中,并以 begindata 连接,还要把 infile "users_data.csv" 改为 infile *。同时为了更大化的说明问题,把数据处理了一下。此时,完整的 users.ctl 文件内容是:
<code>03.</code><code>infile * </code><code>-- 因为数据同控制文件在一起,所以用 * 表示</code>
<code>04.</code><code>append </code><code>-- 这里用了 append 来操作,在表 users 中附加记录 </code>
<code>05.</code><code>into</code> <code>table</code> <code>users</code>
<code>06.</code><code>when</code> <code>login_times<></code><code>'8'</code> <code>-- 还可以用 when 子句选择导入符合条件的记录</code>
<code>07.</code><code>fields terminated</code><code>by</code> <code>","</code>
<code>08.</code><code>trailing nullcols</code>
<code>09.</code><code>(</code>
<code>10.</code><code> </code><code>virtual_column filler,</code><code>--跳过由 pl/sql developer 生成的第一列序号</code>
<code>11.</code><code> </code><code>user_id</code><code>"user_seq.nextval"</code><code>,</code><code>--这一列直接取序列的下一值,而不用数据中提供的值</code>
<code>12.</code><code> </code><code>user_name</code><code>"'hi '||upper(:user_name)"</code><code>,</code><code>--,还能用sql函数或运算对数据进行加工处理</code>
<code>13.</code><code> </code><code>login_times terminated</code><code>by</code> <code>","</code><code>,</code><code>nullif</code><code>(login_times=</code><code>'null'</code><code>)</code><code>--可为列单独指定分隔符</code>
<code>14.</code><code> </code><code>last_login</code><code>date</code> <code>"yyyy-mm-dd hh24:mi:ss"</code> <code>nullif</code> <code>(last_login=</code><code>"null"</code><code>)</code><code>-- 当字段为"null"时就是 null</code>
<code>15.</code><code>)</code>
<code>16.</code><code>begindata</code><code>--数据从这里开始</code>
<code>17.</code><code> </code><code>,user_id,user_name,login_times,last_login</code>
<code>18.</code><code>1,1,unmi,3,2009-1-5 20:34</code>
<code>19.</code><code>2,2,fantasia,5,2008-10-15</code>
<code>20.</code><code>3,3,隔叶黄莺,8,2009-1-2</code>
<code>21.</code><code>4,4,kypfos,</code><code>null</code><code>,</code><code>null</code>
<code>22.</code><code>5,5,不知秋,1,2008-12-23</code>
2) 执行一样的命令:
比如,在控制台会显示这样的信息:
sql*loader: release 9.2.0.1.0 - production on 星期三 1月 7 22:26:25 2009
copyright (c) 1982, 2002, oracle corporation. all rights reserved.
达到提交点,逻辑记录计数4
达到提交点,逻辑记录计数5
上面的控制文件包含的内容比较复杂(演示目的),请根据注释理解每个参数的意义。还能由此发掘更多用法。
最后说下有关 sql *loader 的性能与并发操作
1) rows 的默认值为 64,你可以根据实际指定更合适的 rows 参数来指定每次提交记录数。(体验过在 pl/sql developer 中一次执行几条条以上的 insert 语句的情形吗?)
2)常规导入可以通过使用 insert语句来导入数据。direct导入可以跳过数据库的相关逻辑(direct=true),而直接将数据导入到数据文件中,可以提高导入数据的性能。当然,在很多情况下,不能使用此参数(如果主键重复的话会使索引的状态变成unusable!)。
3) 通过指定 unrecoverable选项,可以关闭数据库的日志(是否要 alter table table1 nologging 呢?)。这个选项只能和 direct 一起使用。
4) 对于超大数据文件的导入就要用并发操作了,即同时运行多个导入任务.
sqlldr userid=/ control=result1.ctl direct=true parallel=true
sqlldr userid=/ control=result2.ctl direct=true parallel=true
当加载大量数据时(大约超过10gb),最好抑制日志的产生:
sql>alter table resultxt nologging;
这样不产生redo log,可以提高效率。然后在 control 文件中 load data 上面加一行:unrecoverable, 此选项必须要与direct共同应用。
在并发操作时,oracle声称可以达到每小时处理100gb数据的能力!其实,估计能到 1-10g 就算不错了,开始可用结构 相同的文件,但只有少量数据,成功后开始加载大量数据,这样可以避免时间的浪费。
general
note: this page consists of a series of demonstrations of various sql*loader capabilities. it is by no means complete.
for the oracle doc:
<a href="http://download-west.oracle.com/docs/cd/b19306_01/server.102/b14215/app_ldr_syntax.htm#i631434">http://download-west.oracle.com/docs/cd/b19306_01/server.102/b14215/app_ldr_syntax.htm#i631434</a>
sql loader data types
char
decimal external
integer external
modes
append
insert
replace
truncate
infile
infile * or infile '<file_name>'
[recsize <integer> buffers <integer>]
infile 'mydata.dat' "recsize 80 buffers 8"
into
into <table_name>
into table emp
badfile
records with formatting errors or that cause oracle errors
badfile '<file_name>'
badfile 'sample.bad'
discardfile
records not satisfying a when clause
discardfile '<file_name>'
discardmax <integer>
discardfile 'sample.dsc'
characterset
characterset <character_set_name>
characterset we8mswin1252
length
length semantics <byte | char>
length semantics byte
-- this is the default for all character sets except utf16
load types
options clause
bindsize = n
columnarrayrows = n
direct = {true | false}
errors = n
load = n
multithreading = {true | false}
parallel = {true | false}
readsize = n
resumable = {true | false}
resumable_name = 'text string'
resumable_timeout = n
rows = n
silent = {header | feedback | errors | discards | partitions | all}
skip = n
skip_index_maintenance = {true | false}
skip_unusable_indexes = {true | false}
streamsize = n
options (bindsize=100000, silent=(errors, feedback))
paths
conventional path
direct path
all loads demonstrated below are convention with the exception of demo 6.
terminators
comma
','
tab
0x'09'
trailing nullcols
-- assuming this data
10 accounting
-- the following
into table dept
trailing nullcols
( deptno char terminated by " ",
dname char terminated by whitespace,
loc char terminated by whitespace)
-- would generate an error without trailing nullcols
-- as it doesn't have loc data
when
when <condition>
see demo 5 below
assembling logical records
concatenate
concatenate <number_of_physical_records>
concatenate 3
continueif
continueif this [preserve] (start_position:end_position) = value
continueif this (1:2) = '%%'
continueif this preserve (1:2) = '%%'
continueif next [preserve] (start_position:end_position) = value
continueif next (1:2) = '%%'
continueif next preserve (1:2) = '%%'
continueif last (start_position:end_position) = value
-- tests against the last non-blank character.
-- allows only a single character for the test
preserve
preserves the continueif characters
demo tables & data
demo tables
create table dept (
deptno varchar2(2),
dname varchar2(20),
loc varchar2(20));
create table emp (
empno number(4),
ename varchar2(10),
job varchar2(10),
mgr number(4),
hiredate date,
sal number(8,2),
comm number(7,2),
deptno number(2),
projno number(4),
loadseq number(3));
create table proj (
emp number(4),
projno number(3));
create table funcdemo (
last_name varchar2(20),
first_name varchar2(20));
create table decodemo (
fld1 varchar2(20),
fld2 varchar2(20));
create table denver_prj (
projno varchar2(3),
empno number(5),
projhrs number(2));
create table orlando_prj (
create table misc_prj (
create table po_tab of xmltype;
create table loadnums(
col1 varchar2(10),
col2 number);
demo 1
basic import of delimited data with data in the control file
<a href="http://writeblog.csdn.net/demo01.ctl">control file</a>
options (errors=500, silent=(feedback))
load data
infile *
into table <table_name>
fields terminated by <delimiter>
optionally enclosed by <enclosing character>
(<column_name>, <column_name>, <column_name>)
sqlldr userid=uwclass/uwclass control=c:/load/demo01.ctl log=d:/load/demo01.log
demo 2
basic import of fixed length data with separate data and control files
<a href="http://writeblog.csdn.net/demo02.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo02.dat">data file</a>
infile <data_file_path_and_name>
into table <table_name> (
<column_name> position(<integer>:<integer>) <data_type>,
<column_name> position(<integer>:<integer>) <data_type>)
sqlldr userid=uwclass/uwclass control=c:/load/demo02.ctl log=c:/load/demo02.log
demo 3
append of delimited data with data in the control file. this sample demonstrates date formating, delimiters within delimiters and implementation of record numbering with a sql*loader sequence. append indicates that the table need not be empty before the sql*loader is run.
<a href="http://writeblog.csdn.net/demo03.ctl">control file</a>
fields terminated by ","
optionally enclosed by '"'
(<column_name>, <column_name> date "dd-month-yyyy",
<column_name> char terminated by ':',
<column_name> sequence(max,1))
sqlldr userid=uwclass/uwclass control=c:/load/demo03.ctl log=c:/load/demo3.log
demo 4
replace of fixed length data with separate data and control file. this sample demonstrates specifying a discard file, the maximum number of records to discard (discardmax), and continueif ( where it looks for an asterisk in the first position to determine if a new line has started.
<a href="http://writeblog.csdn.net/demo04.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo04.dat">data file</a>
infile 'c:/temp/demo04.dat'
discardfile 'c:/temp/demo4.dsc'
discardmax 999
continueif this (1) = '*'
into table emp (
empno position(1:4) integer external,
ename position(6:15) char,
hiredate position(52:60) integer external)
sqlldr userid=uwclass/uwclass control=c:/load/demo04.ctl log=c:/load/demo4.log
demo 5
loading into multiple tables during an import using the when keyword. the control file loads two different tables making three passes at one of them. note the problem with the doolittle record and how it is handled.
<a href="http://writeblog.csdn.net/demo05.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo05.dat">data file</a>
infile 'c:/temp/demo05.dat'
badfile 'c:/temp/bad05.bad'
discardfile 'c:/temp/disc05.dsc'
empno position(1:4) integer external,
ename position(6:15) char,
deptno position(17:18) char,
mgr position(20:23) integer external)
--1st project: proj has two columns, both not null
into table proj
when projno != ' ' (
emp position(1:4) integer external,
projno position(25:27) integer external)
-- 2nd project
projno position(29:31) integer external)
-- 3rd project
projno position(33:35) integer external)
sqlldr userid=uwclass/uwclass control=c:/load/demo5.ctl log=d:/load/demo5.log
demo 6
using the nullif and blanks keywords to handle zero length strings being loaded into numeric columns. also note the use of direct path load in the control file (direct=true).
<a href="http://writeblog.csdn.net/demo06.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo06.dat">data file</a>
infile 'c:/temp/demo06.dat'
-- sorted indexes (emp_empno)
(
empno position(01:04) integer external nullif empno=blanks,
ename position(06:15) char,
job position(17:25) char,
mgr position(27:30) integer external nullif mgr=blanks,
sal position(32:39) decimal external nullif sal=blanks,
comm position(41:48) decimal external nullif comm=blanks,
deptno position(50:51) integer external nullif deptno=blanks)
sqlldr userid=uwclass/uwclass control=c:/load/demo06.ctl log=c:/load/demo06.log direct=true
demo 7
using a buit-in function to modify data during loading
<a href="http://writeblog.csdn.net/demo07.ctl">control file</a>
into table funcdemo
last_name position(1:7) char "upper(:last_name)",
first_name position(8:15) char "lower(:first_name)"
)
begindata
locke phil
gorman tim
sqlldr userid=uwclass/uwclass control=c:/load/demo07.ctl log=c:/load/demo07.log
demo 8
another example of using a built-in function, in this case decode, to modify data during loading
<a href="http://writeblog.csdn.net/demo08.ctl">control file</a>
into table decodemo
fields terminated by ','
fld1,
fld2 "decode(:fld1, 'hello', 'goodbye', :fld1)"
hello,""
goodbye,""
this is a test,""
sqlldr userid=uwclass/uwclass control=c:/load/demo08.ctl log=c:/load/demo08.log
demo 9
loading multiple files into multiple tables in a singe control file. note the use of the when keyword.
<a href="http://writeblog.csdn.net/demo09.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo09a.dat">data file</a>
<a href="http://writeblog.csdn.net/demo09b.dat">data file</a>
infile 'c:/temp/demo09a.dat'
infile 'c:/temp/demo09b.dat'
into table denver_prj
when projno = '101' (
projno position(1:3) char,
empno position(4:8) integer external,
projhrs position(9:10) integer external)
into table orlando_prj
when projno = '202' (
into table misc_prj
when projno != '101' and projno != '202' (
sqlldr userid=uwclass/uwclass control=c:/load/demo09.ctl log=c:/load/demo09.log
demo 10
loading negative numeric values. note clark and miller's records in the data file. note empty row
<a href="http://writeblog.csdn.net/demo10.ctl">control file</a>
<a href="http://writeblog.csdn.net/demo10.dat">data file</a>
infile 'c:/temp/demo10.dat'
reject rows with all null fields
empno position(01:04) integer external,
mgr position(27:30) integer external,
sal position(32:39) decimal external,
comm position(41:48) decimal external,
deptno position(50:51) integer external)
sqlldr userid=uwclass/uwclass control=c:/load/demo10.ctl log=c:/load/demo10.log
demo 11
loading xml
<a href="http://writeblog.csdn.net/demo11.ctl">control file</a>
into table po_tab
xmltype (xmldata)
fields
(xmldata char(2000))
desc po_tab
sqlldr userid=uwclass/uwclass control=c:/load/demo11.ctl log=c:/load/demo11.log
set long 1000000
select * from po_tab;
select *
from po_tab
where sys_nc_rowinfo$ like '%hurry%';
demo 12
loading a constant, recnum, and sysdate
<a href="http://writeblog.csdn.net/demo12.ctl">control file</a>
options (errors=100, silent=(feedback))
into table dept
(recno recnum, deptno constant "xx", dname, loc, tdate sysdate)
alter table dept
add (recno number(5), tdate date);
desc dept
sqlldr userid=uwclass/uwclass control=c:/load/demo12.ctl log=c:/load/demo12.log
demo 13
setting readsize and bindsize
the control file and data for
this demo can be found in
/demo/schema/sales_history/
schema under $oracle_home
as cust1v3.ctl and cust1v3.dat
bindsize and readsize
do not apply to direct path loads
infile 'c:/temp/cust1v3.dat'
into table customers
fields terminated by '|'
(cust_id, cust_first_name, cust_last_name, cust_gender, cust_year_of_birth, cust_marital_status, cust_street_address, cust_postal_code, cust_city, cust_city_id, cust_state_province, cust_state_province_id, country_id, cust_main_phone_number, cust_income_level, cust_credit_limit, cust_email, cust_total, cust_total_id, cust_src_id, cust_eff_from date(19) "yyyy-mm-dd-hh24-mi-ss", cust_eff_to date(19) "yyyy-mm-dd-hh24-mi-ss", cust_valid)
conn sh/sh
grant select on customers to uwclass;
conn uwclass/uwclass
create table customers as
select * from sh.customers
where 1=2;
desc customers
-- run 1 - default sizing
sqlldr userid=uwclass/uwclass control=c:/load/demo13.ctl log=c:/load/demo13.log
space allocated for bind array: 251252 bytes(46 rows)
read buffer bytes: 1048576
elapsed time was: 00:00:03.73
cpu time was: 00:00:01.25
-- run 2 - double default to 2m
sqlldr userid=uwclass/uwclass control=c:/load/demo13.ctl log=c:/load/demo13.log readsize=2048000 bindsize=2048000 rows=64
space allocated for bind array: 349568 bytes(64 rows)
read buffer bytes: 2048000
elapsed time was: 00:00:03.50
cpu time was: 00:00:01.09
-- run 3 - double default to 4m
sqlldr userid=uwclass/uwclass control=c:/load/demo13.ctl
log=c:/load/demo13.log readsize=4096000 bindsize=4096000 rows=64
read buffer bytes: 4096000
elapsed time was: 00:00:03.65
cpu time was: 00:00:01.12
demo 14
trailing
load numbers with trailing + and - signs
into table loadnums (
col1 position(1:5),
col2 position(7:16) "to_number(:col2,'99,999.99mi')")
abcde 1,234.99-
abcde 11,234.34+
abcde 45.23-
abcde 99,234.38-
abcde 23,234.23+
abcde 98,234.23+
sqlldr userid=uwclass/uwclass control=c:/load/demo14.ctl log=c:/load/demo09.log