本来项目所用的数据库从Sybase转为Oracle, 导数据是用户他们自己的责任, 所以导数据我是不用管的, 也不在我的工作范围内, 但是有机会能学下BCP和SQLLDR还是很好的, 唉, 晚上的时间没啦! T.T (本人所用系统是XP)
在上篇文章Sybase脚本修改为oracle脚本备忘 中, 把表结构先建立好, 但是先别建立主键, 外键, 索引, 并建立下面的文件夹.
bcp.txt, bcpout.bat, sqlldr.txt, sqlldrout.bat 放于: E:/sybase_temp/
数据库数据文件放于: E:/sybase_temp/data/
ctl文件放于: E:/sybase_temp/ctl/
log文件放于: E:/sybase_temp/log/
error_log文件放于: E:/sybase_temp/error/
其中: bcp.txt是生成bcpout.bat的脚本, bcpout.bat是把sybase的用户表数据导出为文件的程序, sqlldr.txt是生成sqlldrout.bat的脚本, sqlldrout.bat是把导出的文件中的数据导入到oracle中的表里面的程序.
准备工作做好后, 那就开始啦~~~~~~~~~
1.
可用如下方法生成一个可以一次导出一个数据库中所有表的数据的执行脚本。
编辑一个如下文本文件 文件名称例为 E:/sybase_temp/bcp.txt :
use database1 --用到的数据库
go
select 'bcp database1..' + name + ' out E:/sybase_temp/data/' + name + '.txt -U user1 -P pw1 -S server1 -c -t"|,>|" -r "<~|>" ' from sysobjects where type = 'U'
--bcp database1..CMS_CATEGORY out E:/sybase_temp/data/CMS_CATEGORY.txt -U user1 -P dw1 -S server1 -c -t"|,>|" -r "<~|>"
-- -U 用户名 -P 密码 -S 服务器名 -t字段分隔符(默认是制表符/t, 不过还是要用复杂点的好) -r行分隔符(默认是回车符/n, 如果用默认的,字段中存在回车符将导致数据错误)
go
注意: E:/sybase_temp/data 文件夹必须要存在才行
在dos里面执行:
isql -U user1 -P pw1 -S server1 -i E:/sybase_temp/bcp.txt -o E:/sybase_temp/bcpout.bat
i参数后为输入文件,o参数后文件为输入文件执行后得到的输出文件。
执行后可以得到一个后缀名为 BAT 的批处理文件(在unix下则生成一个shell文件并更改相应的执行权限),可直接执行。执行后将在指定的目录下导出了相应的TXT(保存为TXT为个人喜好)数据文件。一个表的数据为一个文件。如在UNIX下则可不用BAT后缀。
2. 执行生成的bcpout.bat文件
在E:/sybase_temp/文件夹下面有很多文件被创建, 这些文件里面都是数据库表的数据, 检查数据库表的数量跟产生的文件的数量是否相符.
3.编辑文本 E:/sybase_temp/sqlldr.txt :
use database1--用到的数据库
go
select 'sqlldr userid=user1/pw1@server1 control=E:/sybase_temp/ctl/' + name + '.ctl log=E:/sybase_temp/log/' + name + '.log bad=E:/sybase_temp/error/' + name + '_error.log ' from sysobjects where type = 'U'
--sqlldr userid=user1/pw1@server1 control=E:/sybase_temp/ctl/CMS_CATEGORY.ctl log=E:/sybase_temp/log/CMS_CATEGORY.log bad=E:/sybase_temp/error/CMS_CATEGORY_error.log
go
注意: E:/sybase_temp/ctl/, E:/sybase_temp/log/ , E:/sybase_temp/error/ 文件夹必须要存在才行
在dos里面执行:
isql -U user1 -P pw1 -S server1 -i E:/sybase_temp/sqlldr.txt -o E:/sybase_temp/sqlldrout.bat
4. 在项目在写一个生成ctl文件的java类(还有很多方法的), sybase jdbc驱动是jconn2.jar
SybaseTableCtl.java
编译执行main方法, 如果没有出错的话,
生成如E:/sybase_temp/ctl/CMS_CATEGORY.ctl这样的文件
load data
infile 'E:/sybase_temp/data/CMS_CATEGORY.txt' "str '<~|>'"
truncate into table CMS_CATEGORY
fields terminated by '|,>|'
trailing nullcols
(CATEGORY_ID,CATEGORY_NAME,CATEGORY_DESC,
CREATE_DATE "TO_DATE(TO_CHAR(TO_TIMESTAMP(:CREATE_DATE, 'MON-DD-YYYY HH:MI:SS:FF AM','NLS_DATE_LANGUAGE=AMERICAN'),'MON DD YYYY HH24:MI:SS'),'MON DD YYYY HH24:MI:SS')" )
(今天突然想到为什么我要连接sybase呢, 连接oracle也是可以的, 并且可以直接判断字段的类型, 再加于修改)
5. 执行生成的sqlldrout.bat文件
检查error和log文件夹下面的文件, 注意如果ctl控制文件的脚本有错误的话, 那error可能没有log通知的.
导入数据后记得把主键,外键和索引都建上.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
整个过程中最难搞的是日期的部分, 在字段field 后面加
"TO_DATE(TO_CHAR(TO_TIMESTAMP(:field, 'MON-DD-YYYY HH:MI:SS:FF AM','NLS_DATE_LANGUAGE=AMERICAN'),'MON DD YYYY HH24:MI:SS'),'MON DD YYYY HH24:MI:SS')"
注意'NLS_DATE_LANGUAGE=AMERICAN' , 不然第一个to_timestamp都不能转换, NLS_DATE_LANGUAGE默认是SIMPLIFIED CHINESE, 识别不了Det,Oct月份这样的写法
对字段类型是dete和timestamp都可以,如果毫秒对你还有用的话, 那就要对这两个类型加以判断, 再进行转换
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
----------------------------------------------------------------可爱的分割线---------------------------------------------------------------------
可能出现的问题:
1. 数据库连接不上
2. 无法获取文件, 原因是ctl文件里面的infile文件路径或文件名不对
3. 有些值太大了, 导致字段无法导入
4. 其他问题
参考文章:
http://www.itpub.net/viewthread.php?tid=716825&extra=&page=3
http://www.blogjava.net/Unmi/archive/2009/01/05/249956.aspx?opt=admin
http://unixboy.javaeye.com/blog/191742
http://www.chinaunix.net/jh/21/20273.html
----------------------------------------------------------------可爱的分割线---------------------------------------------------------------------
控制文件相关说明:
--OPTIONS(DIRECT=TRUE,skip=0,ERRORS=0,READSIZE=655360)
load data --这个固定, 并且必要
infile 'E:/sybase_temp/data/ZJK_PJZJXX.txt' "str '<~|>'" --要导入的数据文件, 可以写多个infile, "str '行分隔符'" 指定行的分隔符的
truncate into table ZJK_PJZJXX --导入的表
fields terminated by ',|,,' --指定字段分隔符
trailing nullcols
(ID,ID_HD,ID_ZJ,PJJB,SCORE,REMARK,TIME_PJ TIMESTAMP 'MON DD YYYY HH:MI:SS:FF AM' , LOGIN_NAME,PJR)
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
根据用户下所有表生成控制文件
--在操作系统上生成文件
CREATE OR REPLACE PROCEDURE sp_Write_log(text_context VARCHAR2,p_filename VARCHAR2)
IS
file_handle utl_file.file_type;
Write_content VARCHAR2(32767);
Write_file_name VARCHAR2(1000);
BEGIN
--open file
write_file_name := p_filename;
file_handle := utl_file.fopen('UTL_DIR',write_file_name,'w');
write_content := text_context;
--write file
IF utl_file.is_open(file_handle) THEN
utl_file.put_line(file_handle,write_content);
dbms_output.put_line(write_content);
END IF;
--close file
utl_file.fclose(file_handle);
EXCEPTION
WHEN OTHERS THEN
BEGIN
IF utl_file.is_open(file_handle) THEN
utl_file.fclose(file_handle);
END IF;
END;
END sp_Write_log;
/
--生成单个表sqlldr控制文件存储过程
CREATE OR REPLACE PROCEDURE P_generate_sqlldr_null(p_table_name IN VARCHAR2) AS
l_curr_line LONG;
l_table_name user_tables.table_name%TYPE;
BEGIN
select table_name
into l_table_name
from user_tables
where table_name =upper(p_table_name);
l_curr_line := '
LOAD DATA
INFILE '''||lower(l_table_name)||'.txt'||''' '||'"'||'str X'||'''7C0D0A'''||'"'||'
INTO TABLE ';
l_curr_line := l_curr_line||l_table_name||'
FIELDS TERMINATED BY '||''''||'|/t'||''''||'
TRAILING NULLCOLS
(';
for rec in ( select table_name,column_name,column_id,nullable
from user_tab_columns
where table_name =upper(p_table_name)
order by column_id) loop
if rec.column_id = 1 THEN
IF rec.nullable = 'Y' THEN
l_curr_line := l_curr_line||'
'||rpad(rec.column_name||' NULLIF '||rec.column_name||'='||'''//N''',80);
ELSE
l_curr_line := l_curr_line||'
'||rpad(rec.column_name,80);
END IF;
ELSE
IF rec.nullable = 'Y' THEN
l_curr_line := l_curr_line||'
,'||rpad(rec.column_name||' NULLIF '||rec.column_name||'='||'''//N''',80);
ELSE
l_curr_line := l_curr_line||'
,'||rpad(rec.column_name,80);
END IF;
end if;
end loop;
l_curr_line := l_curr_line||')';
sp_write_log(l_curr_line,l_table_name||'.ctl');
END P_generate_sqlldr_null;
/
--全部生成用户下所有表控制文件并且放在操作系统中指定的目录中
CREATE OR REPLACE PROCEDURE p_gener_user_sqlldr AS
BEGIN
FOR x IN (SELECT table_name FROM user_tables) LOOP
P_GENERATE_SQLLDR_null(x.table_name);
END LOOP;
END p_gener_user_sqlldr;
/
select SYS_CONTEXT('USERENV','NLS_DATE_FORMAT') DF, SYS_CONTEXT('USERENV','NLS_DATE_LANGUAGE') DL from dual;
alter session set NLS_DATE_LANGUAGE=AMERICAN; --SIMPLIFIED CHINESE
alter session set NLS_DATE_FORMAT='yyyy-mon-dd';--DD-MON-RR
--select to_date('Oct 6 2008 6:39:44:000AM', 'rr-MM-dd hh24:mi:ss','nls_date_language=american') from dual;
select to_timestamp('Oct 6 2008 6:39:44:000AM', 'Mon dd YYYY hh:mi:ss:ff AM') from dual;