You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中对比两个Oracle数据库的表并找出差异?

跨Oracle数据库表数据对比方案(Java实现)

核心思路

以**主键(如UserID)**作为唯一标识,将两个数据库的表数据转换为「主键-行数据」的Map结构,通过对比Map找出三类差异:

  1. 仅在数据库1存在的行
  2. 仅在数据库2存在的行
  3. 主键相同但字段数据不同的行

这种方式对比效率高(时间复杂度O(n)),且能精准定位差异细节。

具体实现步骤

1. 定义数据模型类

对应表结构创建实体类,重写equals、hashCode和toString方法,方便后续对比和打印:

import java.util.Objects;

public class CustomerOrder {
    private String userId;
    private String customer;
    private String order;
    private Integer number;

    // 构造方法
    public CustomerOrder(String userId, String customer, String order, Integer number) {
        this.userId = userId;
        this.customer = customer;
        this.order = order;
        this.number = number;
    }

    // Getter方法(略)

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CustomerOrder that = (CustomerOrder) o;
        return Objects.equals(userId, that.userId) &&
                Objects.equals(customer, that.customer) &&
                Objects.equals(order, that.order) &&
                Objects.equals(number, that.number);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, customer, order, number);
    }

    @Override
    public String toString() {
        return String.format("UserID: %s, Customer: %s, Order: %s, Number: %d",
                userId, customer, order, number);
    }
}

2. 加载数据库数据到Map

编写通用方法,从指定数据库加载表数据,返回以UserID为key的Map:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

private static Map<String, CustomerOrder> loadTableData(Connection conn, String tableName) throws SQLException {
    Map<String, CustomerOrder> dataMap = new HashMap<>();
    // 仅查询需要对比的字段,避免SELECT *带来的冗余数据传输
    String sql = "SELECT UserID, Customer, \"Order\", number FROM " + tableName;
    
    try (PreparedStatement pstmt = conn.prepareStatement(sql);
         ResultSet rs = pstmt.executeQuery()) {
        while (rs.next()) {
            CustomerOrder row = new CustomerOrder(
                    rs.getString("UserID"),
                    rs.getString("Customer"),
                    rs.getString("Order"),
                    rs.getInt("number")
            );
            dataMap.put(row.getUserId(), row);
        }
    }
    return dataMap;
}

3. 对比两个Map并输出差异

编写对比逻辑,遍历两个Map找出三类差异并打印:

public static void compareTwoDatabases(Connection conn1, Connection conn2) throws SQLException {
    Map<String, CustomerOrder> db1Data = loadTableData(conn1, "Table_All_Customers");
    Map<String, CustomerOrder> db2Data = loadTableData(conn2, "Table_All_Customers"); // 库2表名不同则修改参数

    // 1. 仅数据库1存在的行
    System.out.println("=== 仅数据库1存在的记录 ===");
    for (String userId : db1Data.keySet()) {
        if (!db2Data.containsKey(userId)) {
            System.out.println(db1Data.get(userId));
        }
    }

    // 2. 仅数据库2存在的行
    System.out.println("\n=== 仅数据库2存在的记录 ===");
    for (String userId : db2Data.keySet()) {
        if (!db1Data.containsKey(userId)) {
            System.out.println(db2Data.get(userId));
        }
    }

    // 3. 主键相同但数据不同的行
    System.out.println("\n=== 主键相同但数据不一致的记录 ===");
    for (String userId : db1Data.keySet()) {
        if (db2Data.containsKey(userId)) {
            CustomerOrder row1 = db1Data.get(userId);
            CustomerOrder row2 = db2Data.get(userId);
            if (!row1.equals(row2)) {
                System.out.println("数据库1: " + row1);
                System.out.println("数据库2: " + row2);
                System.out.println("------------------------");
            }
        }
    }
}

4. 主方法调用示例

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBComparer {
    private static final String DB1_URL = "jdbc:oracle:thin:@//db1-host:1521/ORCL";
    private static final String DB1_USER = "your_db1_user";
    private static final String DB1_PASS = "your_db1_pass";

    private static final String DB2_URL = "jdbc:oracle:thin:@//db2-host:1521/ORCL";
    private static final String DB2_USER = "your_db2_user";
    private static final String DB2_PASS = "your_db2_pass";

    public static void main(String[] args) {
        try (Connection conn1 = DriverManager.getConnection(DB1_URL, DB1_USER, DB1_PASS);
             Connection conn2 = DriverManager.getConnection(DB2_URL, DB2_USER, DB2_PASS)) {
            compareTwoDatabases(conn1, conn2);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 上述loadTableData、compareTwoDatabases方法放在这里
}

高效优化建议

  1. 主键索引:确保两个表的UserID字段为主键或有唯一索引,提升数据查询速度。
  2. 分批加载:如果表数据量极大(百万级以上),使用Oracle分页语法(如FETCH NEXT 1000 ROWS ONLY)分批加载,避免内存溢出。
  3. 并行加载:用多线程同时加载两个数据库的数据,减少总加载时间。
  4. 哈希对比:若仅需快速判断是否有差异,可计算每行的MD5哈希值存储到Map,对比哈希值比对比全字段更快(需注意哈希碰撞概率极低,可忽略)。

你之前代码的问题说明

  • 嵌套遍历ResultSet导致第二个ResultSet仅被读取一次,指针到末尾后无法再获取数据;
  • 仅对比UserID列表,未检查其他字段的差异;
  • 使用List存储数据,对比时时间复杂度为O(n²),效率远低于Map的O(n)。

内容的提问来源于stack exchange,提问作者Rio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 17:17:43