如何在Java代码中解析ORA-01795

我在Java代码中执行ORA-01795错误,同时在IN子句中执行超过1000条记录。 我正在考虑使用由OR子句分隔的多个IN子句在1000个条目的批处理中打破它,如下所示:

  select * from table_name where column_name in (V1,V2,V3,...V1000) or column_name in (V1001,V1002,V1003,...V2000) 

我有一个字符串ID,如-18435,16690,1719,1082,1026,100759...它是根据用户选择动态生成的。 如何在Java中为1-1000条记录,1001条到2000条记录等条件编写逻辑。 有人能帮我一下吗?

围绕此限制有三种可能的方法:

1)正如您已经提到的那样:将批量声明分成1000份

2)使用值创建派生表,然后将它们连接起来:

 with id_list (id) as ( select 'V1' from dual union all select 'V2' from dual union all select 'V3' from dual ) select * from the_table where column_name in (select id from id_list); 

或者您也可以加入这些值 – 甚至可能更快:

 with id_list (id) as ( select 'V1' from dual union all select 'V2' from dual union all select 'V3' from dual ) select t.* from the_table t join id_list l on t.column_name = l.id; 

这仍然会产生一个非常非常巨大的声明,但没有1000 ids的限制。 我不确定甲骨文会解析这个问题的速度有多快。

3)将值插入(全局)临时表,然后使用IN子句(或JOIN )。 这可能是最快的解决方案。

有了这么多的值,我会在查询中尽可能避免inor ,以及嵌入值的硬解析惩罚。 您可以传递SQL值集合,并将table()集合表达式用作可以将实际表连接到的表。

这使用硬编码的整数数组作为示例,但您可以从用户输入填充该数组。 我正在使用内置的集合类型定义 ,比如sys.odcinumberlist ,它们是一个数字的varray ,并且限制为32k值,但是如果您愿意或者可能需要处理更多,则可以定义自己的表类型。

 int[] ids = { -18435,16690,1719,1082,1026,100759 }; ArrayDescriptor aDesc = ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST", conn ); oracle.sql.ARRAY ora_ids = new oracle.sql.ARRAY(aDesc, conn, ids); sql = "select t.* " + "from table(?) a " + "left join table_name t " + "on t.column_name = a.column_value " + "order by id"; pStmt = (OraclePreparedStatement) conn.prepareStatement(sql); pStmt.setArray(1, ora_ids); rSet = (OracleResultSet) pStmt.executeQuery(); ... 

您的数组可以包含任意数量的值(嗯,与您使用的集合类型和JVM的内存可以处理的数量一样多)并且不受列表的1000个成员限制。

本质上, table(?)最终看起来像一个包含所有值的表,这比使用所有值填充真实或临时表并加入其中更容易,更快。

当然,不要真的使用t.* ,列出你需要的列; 我假设你用*来模拟这个问题……

( 这是一个更完整的示例 ,但对于略有不同的情况。)

在这种情况下,当我在Java中的List中有id时,我使用这样的实用程序类将列表拆分为分区并从这些分区生成语句:

 public class ListUtils { public static  List> partition(List orig, int size) { if (orig == null) { throw new NullPointerException("The list to partition must not be null"); } if (size < 1) { throw new IllegalArgumentException("The target partition size must be 1 or greater"); } int origSize = orig.size(); List> result = new ArrayList<>(origSize / size + 1); for (int i = 0; i < origSize; i += size) { result.add(orig.subList(i, Math.min(i + size, origSize))); } return result; } } 

假设您的ID位于名为ids的列表中,您可以通过以下方式获取大小为1000的子列表:

 ListUtils.partition(ids, 1000) 

然后,您可以迭代结果以构造最终的查询字符串。

我最近自己打了这堵墙:

Oracle在IN()中具有最大1000个术语的体系结构限制

有两种解决方法:

  1. 重构查询以成为连接
  2. 保持查询不变,但在循环中多次调用它,每次调用使用少于1000个术语

选项1取决于具体情况。 如果您的值列表来自查询,则可以重构连接

选项2也很简单,但性能较差:

 List terms; for (int i = 0; i <= terms.size() / 1000; i++) { List next1000 = terms.subList(i * 1000, Math.min((i + 1) * 1000, terms.size()); // build and execute query using next1000 instead of terms }