【问题标题】:Optimize the speed on performing "select" query in a big loop优化大循环中执行“选择”查询的速度
【发布时间】:2011-07-05 15:13:04
【问题描述】:

我正在 Java 循环中执行一个简单的“选择”查询,如下所示。列表的大小可以增长到 10000+。如何提高查询速度?任何示例或建议表示赞赏。谢谢。

请注意,我需要检索该表每一列中的所有数据,这就是使用星号 (*) 的原因。

List<String> valueList = ....
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    conn = DriverManager.getConnection(dbURL, dbUsername, dbPassword);
    for (int m = 0; m < valueList.size() ; m++) {    
         String sql = "SELECT * FROM WORKSHEET WHERE " + sheetId + " = '" +    
                      valueList.get(m) + "'";
         ps = conn.prepareStatement(sql);
         rs = ps.executeQuery();
         // retreive data....           
    }
}

编辑:最后,有几种方法可以加快查询速度。我正在使用第二种方式,因为它可以防止将来出现 ORA-04031 错误。

  1. 使用带有“IN”子句的参数化“SELECT”查询。
  2. 创建一个嵌套表并将来自 JDBC 的项的数组/列表转换为创建的嵌套表。
  3. 创建一个临时表并插入项目列表。然后对主表执行 JOIN(1 个查询)并获取结果。

【问题讨论】:

  • SQL Injection Hooooorrrrraaay! 旁注:使用参数化查询也会给你带来更好的性能。
  • 您已经给出了 * 的原因 - 您没有使用参数化查询的任何原因?
  • 通过在每个查询中检索多个 sheetID/m 对来减少执行的查询总数 (WHERE pair1 or pair2 or pair3 etc...) 将减少一些开销。
  • @Bobby:由于我正在构建的是一个 Java 程序,它不需要用户的任何输入,因此 SQL 注入问题不会困扰我。
  • @Jon Skeet:我已经通过实现参数化查询测试了该程序。好像查询速度差不多。

标签: java oracle jdbc


【解决方案1】:

在尝试加快速度时需要考虑两件事:

  1. 对所有 sheetid 只执行一次此查询
  2. 确保您每次都执行相同的查询,而不是硬编码其中的值。 由于这些值可能会发生变化,因此每个查询看起来都与前一个查询相似,但只有几个不同的值。这将不允许 Oracle 重新使用以前的查询并导致共享池中的不可共享 SQL。这将填满共享池。执行此操作的时间足够长,您将收到 ORA-04031 错误消息。

要走的路是使用 SQL 类型。这是 PL/SQL 中的一个示例。您可以在 Java 中使用相同的原理。

首先创建一个带有一万个sheetId的表:

SQL> create table worksheet (sheetid)
  2  as
  3   select level
  4     from dual
  5  connect by level <= 10000
  6  /

Table created.

创建一个 SQL 类型:

SQL> create type mynumbers is table of number;
  2  /

Type created.

在您的代码中,使用“valuelist”中的值填充 SQL 类型的实例,并使用 TABLE 运算符将类型转换为表值:

SQL> declare
  2    valuelist mynumbers := mynumbers(23,124,987,6123,8923,1,7139);
  3  begin
  4    for r in
  5    ( select ws.sheetid
  6        from worksheet ws
  7           , table(valuelist) vl
  8       where ws.sheetid = vl.column_value
  9    )
 10    loop
 11      dbms_output.put_line(r.sheetid);
 12    end loop;
 13  end;
 14  /
1
23
124
987
6123
7139
8923

PL/SQL procedure successfully completed.

现在您的共享池中只有一个 SQL,并且该查询只执行一次,而不是数千次。

【讨论】:

    【解决方案2】:

    时间主要花在准备和执行查询上。

    如果您运行一个返回所有结果的查询,这将使事情变得更快。

    即:

    String where = "(1=0) "
    // first build the where string
    for (int m = 0; m < valueList.size() ; m++ ) {
      where = where + " OR (" + sheetId + " = '" + valueList.get(m) + "'";
      }
    // then run a single query
    sql = "SELECT * FROM WORKSHEET WHERE " + where;
    ps = conn.prepareStatement(sql);
    rs = ps.executeQuery();
    // retrieve and handle data. ....
    

    【讨论】:

    • 嗯...你这么认为吗?他说“List 的大小可以增长到 10000+”
    • 我还是会走这条路!运行每个查询的开销是目前减慢他速度的原因。我认为使用 IN 语句也可以:构建 SQL 来表示 WHERE sheetID IN (1,2,3,4...) 这实际上更易读,而且更短。
    • 我认为通过使用 OR,可以避免 Oracle 中 IN 子句的 1000 项限制,这很好。但我肯定会为所有字符串连接使用 StringBuilder。
    • (1=1)的作用是什么?这是否意味着您获得了所有行,而不是与 sheetIds 匹配的行?
    • @sudocode 我最初使用 OR 的 WHERE 是为了避免 Oracle 中 IN 语句的 1000 条限制。您有一个有效的观点,即构建字符串的其他一些方法看起来会更好。
    【解决方案3】:

    您必须使用 IN 语句准备 sql 查询,然后只执行一次查询...

    【讨论】:

    • 无论如何我试图通过用“OR”和“UNION”拆分它们来解决它。
    【解决方案4】:

    这里有一些其他的想法。

    1. 创建一个临时表并插入您的 (10k) 个列表项。然后对您的主表执行联接(1 个查询)并获取结果。

    2. 创建一个存储过程以将项目列表(通过嵌套表)作为输入并通过 out 参数返回结果集。

    我会选择选项 1。因为它对我来说更直接而且可能更快。但是你需要小心并发会话等。不确定你想如何处理多个会话(他们会共享这些数据,他们会有单独的数据列表吗?)。

    无论如何都要考虑一下。

    【讨论】:

    • 使用全局临时表,Oracle 为您处理并发(每个会话都有自己的空表开始)。
    【解决方案5】:

    Oracle 在IN 子句中最多可以使用1000 个参数。因此,如果您使用准备好的语句,您将减少 1000 倍的迭代次数。只需将列表拆分为 1000 个元素。

    【讨论】:

      【解决方案6】:

      您可以尝试使用子句:sheetId IN ('1', '2', '3', '4') 请确保sheetId 列有索引键。

      【讨论】:

        【解决方案7】:

        我不知道这是否会有所改进,但您可以尝试选择 所有 记录并检查您的 Java 代码是否匹配 sheetId。这是您应该花时间知道什么更好的事情。

        【讨论】:

          【解决方案8】:

          这是一件小事,但如果你要动态构造查询(不使用绑定变量),你应该使用 createStatement,而不是 prepareStatement。 prepareStatement 有少量您不需要的开销。

          【讨论】:

            猜你喜欢
            • 2020-01-19
            • 2018-05-17
            • 2021-03-06
            • 2019-04-22
            • 2013-08-10
            • 2016-04-28
            • 1970-01-01
            • 2022-11-11
            • 2023-03-12
            相关资源
            最近更新 更多