![]() |
#2
netmember2011-03-08 10:54
|
由于ORACLE数据库的种种优点,产生了以前都使用SYBASE做数据库的软件,都转而使用ORACLE数据库,其SYBASE数据转为ORACLE数据类型是一个较为常见的难题,开专帖与各位讨论就教。
![]() |
#2
netmember2011-03-08 10:54
这一问题有一种说法是有点难,有的公司对这种数据的转换工作不予进行,其难度可见闻一斑。
|
![]() |
#3
netmember2011-03-08 18:32
我在网上找到一篇文章,那位仁兄是做过这方面的转换的,帮助一下:
使用BCP和SQLLDR把Sybase中的用户表的数据导到Oracle中备忘【转】2009-04-29 18:39在上篇文章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 import java.sql.*; import java.util.List; import java.util.ArrayList; import import import /** * Created by IntelliJ IDEA. * User: lin * Date: 2009-1-9 * Time: 10:01:05 * To change this template use File | Settings | File Templates. */ public class SybaseTableCtl { public static final int SYBASE_DATABASE = 1; public static final String SYBASE_DATETIME_TYPE = "datetime"; public static final String SYBASE_TIMESTAMP_TYPE = "timestamp"; private int databaseType = 1; public int getDatabaseType() { return databaseType; } public void setDatabaseType(int databaseType) { this.databaseType = databaseType; } public Connection getConn(){ try { //加载JDBC驱动 Class.forName("com.sybase.jdbc2.jdbc.SybDriver"); //创建数据库连接, ***.***.***.***是IP, 自己修改 Connection con = DriverManager.getConnection("jdbc:sybase:Tds:***.***.****.***:5000/database1?FAKE_METADATA=true&charset=cp936&jconnect_version=0", "sa", ""); return con; }catch(ClassNotFoundException cnf){ System.out.println("driver not find:"+cnf); return null; }catch(SQLException sqle){ System.out.println("can?t connection db:"+sqle); return null; } catch (Exception e) { System.out.println("Failed to load JDBC/ODBC driver."); return null; } } public List getTableNameList() throws Exception { List tableNames = null; String sql = getTableSql(); if(sql != null && !"".equals(sql)){ Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ conn = getConn(); if(conn != null){ stmt = conn.createStatement(); rs = stmt.executeQuery(sql); if(rs.next()){ tableNames = new ArrayList(); tableNames.add(rs.getString(1).toUpperCase()); } while(rs.next()){ tableNames.add(rs.getString(1).toUpperCase()); } }else return null; }catch(Exception e){ e.printStackTrace(); return null; }finally{ if(conn != null) conn.close(); } } return tableNames; } /** * * @param dataFilePath 数据文件路径 * @param dataExt 数据库文件扩展名 * @param outFilePath ctl文件输出路径 * @param ext ctl文件扩展名 * @param field_terminator 字段分隔符 * @param row_terminator 行分隔符 * @throws Exception */ public void createCtlTxt(String dataFilePath,String dataExt, String outFilePath, String ext, String field_terminator, String row_terminator) throws Exception { Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ conn = getConn(); if(conn != null){ stmt = conn.createStatement(); List tables = getTableNameList(); if(tables == null){ System.out.println("createCtlTxt: getTableNameList为null, 查询不到用户表"); return ; } String tableName = null; //b.name 字段名, type_name 字段类型名 , 表sysobjects 包括表名的表, syscolumns 包括表字段的表, systypes 表字段类型表 String sql = "select b.name,(select name from systypes where usertype = b.usertype) type_name " + " from sysobjects a, syscolumns b " + " where a.type = 'U' and a.id = b.id " + "and a.name ='"; String txt = ""; System.out.println("createCtlTxt: sql =" + sql); boolean doCretat = false; System.out.println("createCtlTxt: 用户表数目: " + tables.size()); int num = 0; for(int i=0; i < tables.size(); i++){ tableName = (String) tables.get(i); rs = stmt.executeQuery(sql + tableName + "' "); txt = "load data \n " + "infile '" + dataFilePath + File.separator + tableName + "." + dataExt + "' \"str '" + row_terminator + "'\"\n " + " truncate into table " + tableName + " \n " + " fields terminated by '" + field_terminator + "' \n " + " trailing nullcols \n" + "("; String _txt = ""; while(rs.next()){ doCretat = true; String field = rs.getString(1); if(field != null && !"".equals(field)){ _txt += field.toUpperCase(); if(rs.getString(2) != null && rs.getString(2).equals(SybaseTableCtl.SYBASE_DATETIME_TYPE)){ _txt += " \"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')\" ,"; }//---下面这一句没用的, 如果要用到的话, 必须再查询oracle的表的字段类型才有用 else if(rs.getString(2) != null && rs.getString(2).equals(SybaseTableCtl.SYBASE_TIMESTAMP_TYPE)){ _txt += " TIMESTAMP 'YYYY-MM-DD HH:MI:SS:FF AM' ,"; }else{ _txt += ","; } } } if(_txt != null && !"".equals(_txt)){ _txt = _txt.trim(); _txt = _txt.substring(0,_txt.length() -1); //注意不要在 , 后面加空格 } txt += _txt + ")"; if(doCretat){ File outputFile = new File(outFilePath + File.separator + tableName + "." + ext); //创建文件写入类对象,true表示只追加文件,false表示覆盖原文件内容 FileWriter out = new FileWriter(outputFile, false); BufferedWriter bufferOut = new BufferedWriter(out); //写文件内容 bufferOut.write(txt); //关闭文件读写类 bufferOut.close(); out.close(); outputFile = null; out = null; bufferOut = null; num ++; }else{ System.out.println("createCtlTxt: : " + tableName + "no ctl"); } } System.out.println("createCtlTxt: ctl文件建立数目: " + num); }; }catch(Exception e){ e.printStackTrace(); }finally{ if(conn != null) conn.close(); } } public String getTableSql(){ if(getDatabaseType() == SybaseTableCtl.SYBASE_DATABASE){ return "select name from sysobjects where type = 'U' "; }else return ""; } public static void main(String[] args) throws Exception { SybaseTableCtl ctr = new SybaseTableCtl(); ctr.setDatabaseType(SybaseTableCtl.SYBASE_DATABASE); //todo 第一个参数是数据路径, 第二个参数是控制文件路径, 第三个参数是控制文件扩展名, 第四个参数是字段分隔符, 第五个参数是行分隔符 ctr.createCtlTxt("E:\\sybase_temp\\data","txt","E:\\sybase_temp\\ctl","ctl","|,>|", "<~|>"); } } 编译执行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. http://www. http://unixboy. http://www. ----------------------------------------------------------------可爱的分割线--------------------------------------------------------------------- 控制文件相关说明: --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; |
![]() |
#4
gdy03492011-03-08 20:58
sybase我一点也不懂啊
|
![]() |
#5
netmember2011-03-09 19:22
SYBASE用的人家,现在逐渐遭淘汰,这苦了那些现在仍在SYBASE,却要升级成用ORACLE的数据库软件。且数据不保转换。
![]() ![]() |
![]() |
#6
netmember2011-03-11 06:17
版主:
我希望这一贴子,能暂不结帖,以便有人关心这一问题的解决。 |
![]() |
#7
cnfarer2011-03-11 08:08
自己写代码转换比较保险!
|
![]() |
#8
amwihdyt20112011-06-29 03:30
好東西呀,學習了
|