Oracle to Postgres数据传输

时间:2010-09-23 18:15:42

标签: java oracle postgresql jdbc

几年前,我写了一个小实用程序,用于将数据从Oracle数据库移动到Postgres数据库。我使用Java和JDBC来实现这一点,因为我希望Java处理预准备语句中用于执行插入的数据的数据格式。该实用程序的原始版本假定两个数据库中的表名和列名相同。更高版本接受了映射文件来处理名称差异。这个实用程序在我的组织中受到了很大的打击,但不幸的是,它没有扩展。它每小时移动大约一百万行。我们现在拥有超过30万行的表格,没有人愿意等待30个小时来传输数据。

以下方法是实用程序的核心,也是不扩展的原因。此方法对每列数据执行一次,因此会调用它(num_rows * num_cols次)。使用分析器,我发现此方法占用了58%的执行时间。单独的getObject()和findColumn()调用占执行时间的53%!

    public void setPlaceholderValue ( int placeHolderNum, ResultSet rs, String oracleColumnName, PreparedStatement stmt ) throws Exception {

    int columnIndex = rs.findColumn(oracleColumnName) ;
    int columnType = rs.getMetaData().getColumnType(columnIndex) ;

    try{
        if ( rs.getObject(oracleColumnName) != null ){
            switch (columnType) {
                case Types.VARCHAR: stmt.setString(placeHolderNum,  rs.getString(columnIndex)); break;
                case Types.INTEGER:   stmt.setInt(placeHolderNum, rs.getInt(columnIndex)); break ;
                case Types.DATE:       stmt.setDate(placeHolderNum, rs.getDate(columnIndex)); break;
                case Types.FLOAT:      stmt.setFloat(placeHolderNum, rs.getFloat(columnIndex)); break ;
                case Types.NUMERIC:  stmt.setBigDecimal(placeHolderNum,rs.getBigDecimal(columnIndex)); break ;
                case Types.TIMESTAMP:      stmt.setTimestamp(placeHolderNum, rs.getTimestamp(columnIndex)); break ;
                default: throw new SQLException("The result set column type " +  rs.getMetaData().getColumnType(columnIndex) + " was not recognized. see the java.sql.Types class at http://java.sun.com/j2se/1.5.0/docs/api/ ");
            }
        } else {
            stmt.setNull(placeHolderNum, columnType);
        }
    } catch (SQLException e){
        System.out.println ("SQLException: " + e.getMessage() + " for record id=" + rs.getLong("id"));
        throw new SQLException("rethrow");
    }
}

我不确定我是否可以重构此方法以充分缩短传输时间。我认为逐列方法 根本就没有扩展。

有人能建议更好的方法吗?语言不是问题,我可以用任何可以处理的语言来做 工作。理想情况下,我希望每小时至少有1000万条记录的传输率。

4 个答案:

答案 0 :(得分:5)

我建议使用DB提供的导出/导入工具。 Oracle和PostgreSQL支持XML和CSV格式。

如果您希望坚持使用JDBC,请将SELECT ResultSet查询中的列放入与INSERT PreparedStatement查询中的值相同的顺序中{1}}然后执行以下操作而不是整个if/switch块:

preparedStatement.setObject(index, resultSet.getObject(index));

但是,我不认为这会大大提高性能。 DB提供的导出/导入功能可以比Java中的功能更高效。

答案 1 :(得分:1)

这条线可能有问题:

if ( rs.getObject(oracleColumnName) != null ){

改为使用:

if ( rs.getObject(columnIndex) != null ){

在Oracle中,getObject(String)是O(n) - 至少是10g。对于结果集中的每一行,此方法看起来都是为每列调用的。您不应该在每次通话时获取元数据。将与元数据相关的所有调用移动到每个查询一次,并在浏览结果集时将它们传递给此方法。

答案 2 :(得分:0)

您可以尝试创建某种类的类(可能是某种类型的数组)来保存结果集列和类型的信息,这些信息在处理给定结果集时是不变的。然后从数组中提取值,而不是每次需要时调用findColumn和getColumnType。这应该会大大减少对findColumn和getColumnType的调用,并且应该有助于改善运行时。

祝你好运。

答案 3 :(得分:0)

您检索的信息对于该表的整个处理是不变的。

为了每个表只执行一次,您可以创建一个包含列名,列索引,列类型和hasColumn标志成员的数据持有者。传递此数据对象的实例而不是columnName,首次初始化它并将数据用于表的其余部分。像这样兑现数据可以节省2*num_rows*num_cols次检索元数据的电话。