【问题标题】:Best way to select out of millions of rows in an Oracle DB从 Oracle 数据库中的数百万行中进行选择的最佳方法
【发布时间】:2010-09-22 00:19:16
【问题描述】:

生日快乐!

我想在一个有 1500 万行的表中查询一百万个不同的单词。每次查询后都会处理同义词和单词的结果。

表格如下:

    synonym      word
    ---------------------
    ancient      old
    anile        old
    centenarian  old
    darkened     old
    distant      far
    remote       far
    calm         gentle
    quite        gentle

目前在 Java 中是这样完成的:

....
PreparedStatement stmt;
ResultSet wordList;
ResultSet syns;
...

stmt = conn.prepareStatement("select distinct word from table");
wordList = stmt.executeQuery();

while (wordList.next()) {
    stmt = conn.prepareStatement("select synonym from table where word=?");
    stmt.setString(1, wordList.getString(1));
    syns = stmt.executeQuery();

    process(syns, wordList.getString(1));
}
...

这是令人难以置信的慢。做这种事情的最快方法是什么?

干杯, 克里斯

【问题讨论】:

  • 我假设你在 word 列上有一个索引?
  • 是的,单词列上有索引。

标签: java sql oracle select


【解决方案1】:

两个想法:

a) 让它成为一个查询怎么样:

select synonym from table where word in (select distinct word from table)

b) 或者,如果您的process 方法需要将它们作为一个单词的一组同义词来处理,为什么不按word 对它们进行排序,并在每次process 不同时重新开始process?该查询将是:

select word, synonym 
from table 
order by word

【讨论】:

  • 我提炼了我的问题。处理必须在每个单词之后进行。
  • 这将按照它们在原始表中出现的顺序返回所有同义词。它也不会返回单词本身。 “in (select distinct word from table)”只会起到减速的作用。
  • 我有两个选择。我添加了选项 b 的查询
  • 第二点正是它!有时我只是对最简单的解决方案视而不见。配置器也提到了它,但是您提供了代码。这对其他人来说可能更容易遵循,因此:接受并支持两者;)干杯
  • 实际上,根据索引,排序可能只需要很少时间或根本不需要时间。
【解决方案2】:
  1. 确保“单词”列上有索引。

  2. 将第二个 prepareStatement 移到单词循环之外。每次创建新语句时,数据库都会编译并优化查询 - 但在这种情况下,查询是相同的,因此没有必要这样做。

  3. 按照上述sblundy 的方式组合语句。

【讨论】:

    【解决方案3】:

    如果要查询所有同义词,为什么还要查询循环内的同义词?您应该使用单个select word, synonym from table order by word,然后在 Java 代码中按单词拆分。

    【讨论】:

      【解决方案4】:
      PreparedStatement stmt;
      ResultSet syns;
      ...
      
      stmt = conn.prepareStatement("select distinct " + 
                                   "  sy.synonm " + 
                                   "from " +
                                   "  table sy " +
                                   "  table wd " +
                                   "where sy.word = wd.word");
      syns = stmt.executeQuery();
      process(syns);
      

      【讨论】:

        【解决方案5】:

        问题解决了。重要的一点是,表格可以按单词排序。因此,我可以轻松地遍历整个表。像这样:

        ....
        Statement stmt;
        ResultSet rs;
        String currentWord;
        HashSet<String> syns = new HashSet<String>();
        ...
        
        stmt = conn.createStatement();
        rs = stmt.executeQuery(select word, synonym from table order by word);
        
        rs.next();
        currentWord = rs.getString(1);
        syns.add(rs.getString(2));
        
        while (rs.next()) {
            if (rs.getString(1) != currentWord) {
                process(syns, currentWord);
                syns.clear();
                currentWord = rs.getString(1);
            }
            syns.add(rs.getString(2));
        }
        ...
        

        【讨论】:

          【解决方案6】:

          相关但不相关:

          while (wordList.next()) {
              stmt = conn.prepareStatement("select synonym from table where word=?");
              stmt.setString(1, wordList.getString(1));
              syns = stmt.executeQuery();
          
              process(syns, wordList.getString(1));
          }
          

          您应该将该 preparestatement 调用移到循环之外:

          stmt = conn.prepareStatement("select synonym from table where word=?");
          while (wordList.next()) {
              stmt.setString(1, wordList.getString(1));
              syns = stmt.executeQuery();
          
              process(syns, wordList.getString(1));
          }
          

          准备语句的全部目的是让数据库编译/缓存/等,因为您将重复使用该语句。如果要执行这么多查询,您可能还需要明确清理结果集,以确保不会用完游标。

          【讨论】:

            【解决方案7】:

            您还应该考虑利用语句对象的 setFetchSize 方法来减少应用程序和数据库之间的上下文切换。如果您知道要处理一百万条记录,则应使用 setFetchSize(someRelativelyHighNumberLike1000)。这告诉 java 每次需要从 Oracle 获取更多记录时最多抓取 1000 条记录[而不是一次抓取一条记录,这是这种批处理操作的最坏情况]。这将提高您的程序的速度。您还应该考虑重构和对单词/同义词进行批处理,如

            1. 获取 1
            2. 流程 1
            3. 重复

            1. 获取 50/100/1000
            2. 处理 50/100/1000
            3. 重复

            只需将 50/100/1000 [或您一次检索的多个] 保存在某个数组结构中,直到您处理它们。

            【讨论】:

              猜你喜欢
              • 2019-02-15
              • 2012-01-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-07-12
              • 1970-01-01
              • 2020-08-10
              • 1970-01-01
              相关资源
              最近更新 更多