【问题标题】:Fastest way to batch query an ORACLE Database with Java使用 Java 批量查询 ORACLE 数据库的最快方法
【发布时间】:2014-02-24 17:49:53
【问题描述】:

在我正在进行的一个项目中,我得到了一个包含不到 100 万行的列表。数据将所有可能的起点 (000-999) 映射到所有可能的目的地 (000-999)。

对于每个组合,我需要能够查看数据库并确定是否存在具有相同组合的记录。如果不存在记录,则将其添加到数据库中。如果记录确实存在,则将使用新信息更新记录。

origin 和destination 是表的主键,也是索引。 这一切都在 ORACLE 数据库上。

鉴于我必须这样做 100 万次,最好的解决方案是什么? 我目前的方法需要一个多小时来处理所有记录。

为了实际插入和更新记录,我使用了一个不需要太多时间的批量查询过程。

似乎花费最多时间的部分是查询数据库以获取现有记录的计数。

public String batchUpdateModes(List records, String user) throws TransactionException {
    String message = "";
    ArrayList updateList = new ArrayList();
    ArrayList insertList = new ArrayList();
    Connection conn = null;
    try {
        conn = getDao().getConnection();
    } catch (SQLException e1) {
        e1.printStackTrace();
    }
    for (int i = 0; i < records.size(); i++) {
        BatchFileCommand record = (BatchFileCommand)records.get(i);
        String origin = record.getOrigZip().trim();
        String dest = record.getDestZip().trim();
        String pri = record.getPriMode().trim();
        String fcm = record.getFcmMode().trim();
        String per = record.getPerMode().trim();
        String pkg = record.getPkgMode().trim();
        String std = record.getStdMode().trim();
        String effDate = record.getEffDate();
        String discDate = "";

        TransModeObj obj = new TransModeObj(origin, dest, pri, fcm, per, pkg, std, effDate, discDate);
        obj.setUserId(user);
        try {
            Statement stmt = null;
            String findExisting = "select count(*) from trans_mode where orig_zip = " + origin + " " +
                    "and dest_zip = " + dest;
            stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(findExisting);
            int count = 0;
            while (rs.next()) {
                count = rs.getInt(1);
            }
            if (count > 0) {
                updateList.add(obj);
            }
            else {
                insertList.add(obj);
            }
            rs.close();
            stmt.close();


        } catch (SQLException e) {
            e.printStackTrace();
            message = e.getMessage();
        }
    }
    try {
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    boolean success = false;
    recordCount[0] = updateList.size();
    recordCount[1] = insertList.size();
    success = insertTransModes(insertList);
    System.out.println("Inserts Complete");
    success = updateTransModes(updateList);
    System.out.println("Updates Complete");

    if (success) {
        message = "success";
    }
    else {
        message = "The changes or additions submitted could not be completed.";
    }

    return message;

【问题讨论】:

  • 只是和建议,如果您的数据完全格式化,那么您可以简单地使用SQL loader。
  • 带有MERGE 的变体很好,但是如果您从外部源加载大量数据,有很多方法可以提供良好的性能。请查看来自this answer 的链接,尤其是带有SQL*Loader 的变体。

标签: java sql oracle


【解决方案1】:

最简单的解决方案是放弃计数,只使用 MERGE 语句。这允许数据库确定是在单个 SQL 事务中插入还是更新。 Find out more.

MERGE 的一个缺点是行数不区分更新的行和插入的行。这可能是为节省的总时间付出的廉价代价。虽然如果你真的不能没有单独的计数,Adrian Billington has a workaround for you

【讨论】:

    【解决方案2】:

    正如 APC 所提到的 - 当您需要插入或更新时,MERGE 是一个不错的选择。但这可能会更新您不想更新的记录。

    第一个问题是唯一标识您的记录的主键是什么(它是几个字段的组合)?

    另一种方法是预先将所有现有记录的主键加载到内存中,并从记录列表中排除重复项(考虑到您拥有所需数量的 RAM)

    还可以查看thisthat 选项。

    【讨论】:

    • 主键是数据库中索引的起点和终点的组合。
    • 检查提供的链接,希望对您有所帮助。
    • “可能会更新您不想更新的记录。”诶? MERGE 只会更新源数据提供的键中标识的记录。
    • 重点是 MERGE 可能会从提供的数据中更新非关键字段,覆盖那些已经存储在数据库中的字段。
    • 由于我不确定应用程序的逻辑,所以我提到这一刻只是为了说明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 2015-02-03
    • 2013-03-26
    • 2012-03-07
    相关资源
    最近更新 更多