标题:讨论把sybase数据转为oracle数据的方法
只看楼主
netmember
Rank: 2
等 级:论坛游民
帖 子:49
专家分:64
注 册:2011-2-20
结帖率:60%
已结贴  问题点数:20 回复次数:7 
讨论把sybase数据转为oracle数据的方法
由于ORACLE数据库的种种优点,产生了以前都使用SYBASE做数据库的软件,都转而使用ORACLE数据库,其SYBASE数据转为ORACLE数据类型是一个较为常见的难题,开专帖与各位讨论就教。
搜索更多相关主题的帖子: oracle 数据库 软件 
2011-03-08 04:10
netmember
Rank: 2
等 级:论坛游民
帖 子:49
专家分:64
注 册:2011-2-20
得分:0 
这一问题有一种说法是有点难,有的公司对这种数据的转换工作不予进行,其难度可见闻一斑。
2011-03-08 10:54
netmember
Rank: 2
等 级:论坛游民
帖 子:49
专家分:64
注 册:2011-2-20
得分:0 
我在网上找到一篇文章,那位仁兄是做过这方面的转换的,帮助一下:
使用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;
 

2011-03-08 18:32
gdy0349
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:上海
等 级:版主
威 望:11
帖 子:223
专家分:676
注 册:2008-11-12
得分:10 
sybase我一点也不懂啊
2011-03-08 20:58
netmember
Rank: 2
等 级:论坛游民
帖 子:49
专家分:64
注 册:2011-2-20
得分:0 
SYBASE用的人家,现在逐渐遭淘汰,这苦了那些现在仍在SYBASE,却要升级成用ORACLE的数据库软件。且数据不保转换。
2011-03-09 19:22
netmember
Rank: 2
等 级:论坛游民
帖 子:49
专家分:64
注 册:2011-2-20
得分:0 
版主:
 我希望这一贴子,能暂不结帖,以便有人关心这一问题的解决。
2011-03-11 06:17
cnfarer
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:179
帖 子:3330
专家分:21157
注 册:2010-1-19
得分:10 
自己写代码转换比较保险!

★★★★★为人民服务★★★★★
2011-03-11 08:08
amwihdyt2011
Rank: 1
等 级:新手上路
帖 子:12
专家分:0
注 册:2011-2-17
得分:0 
好東西呀,學習了
2011-06-29 03:30



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-333524-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.209602 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved