2011-07-05 53 views
7

我在Java循环中执行一个简单的“select”查询,如下所示。列表的大小可以增长到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查询)并获得结果。
+9

[SQL注入Hooooorrrrraaay!](http://bobby-tables.com/)*旁注:*使用参数化查询也会给你更好的性能。 – Bobby

+2

您已经给出了*的原因 - 您没有使用参数化查询的任何原因? –

+0

通过在每个查询('WHERE pair1或pair2或pair3 etc ...')中检索多个sheetID/m对来减少执行的查询的总数将会减少一些开销。 –

回答

6

有试图加快这时候要考虑两件事情:

  1. 所有sheetid的
  2. 执行这个查询只有一次请确保您正在执行每一次相同的查询,通过不硬编码值in。 由于这些值可能会发生变化,因此每个查询都将看起来像上一个查询,但只有几个不同的值。这将不允许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. 

在你的代码,在你的“值列表填充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在你的共享池中,只需执行一次这个查询,而不是数千。

4

时间主要用于准备和执行查询。

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

即:

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. .... 
+1

嗯......你是这么认为的吗?他说:“名单的规模可以增长到10000+” –

+1

我仍然会这样做!运行每个查询的开销是当前放缓他的成本。 我认为去一个IN语句也会工作:建立SQL来说WHERE sheetID IN(1,2,3,4 ...)这实际上更漂亮,阅读更短。 – Eljakim

+0

我认为通过使用OR,可以避免Oracle中IN子句的1000个项目限制,这很好。但是我肯定会为所有的String连接使用一个StringBuilder。 – sudocode

3

您必须用statment准备SQL查询,然后执行查询只有一次......

+0

IN声明由于1000的限制而无法工作。 –

+0

无论如何,我试图用'OR'和'UNION'来解决它。 –

0

我不知道这是否是一种进步,但你可以尝试如果sheetId匹配,则选择全部记录并使用您的Java代码检查。这是你应该知道什么是更好的。

1

Oracle可能会在IN子句中占用多达1000个参数。所以,如果你使用准备好的语句,你会减少1000倍的迭代次数。只需将它分成1000个元素。

2

这里有一些其他的想法。

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

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

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

无论如何要考虑的事情。

+1

使用全局临时表,Oracle为您处理并发(每个会话都有自己的空表开始)。 –

0

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

1

你可以试一下子句:sheetId IN ('1', '2', '3', '4') 请确保sheetId列有一个索引键。