如何在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
)。 这可能是最快的解决方案。
有了这么多的值,我会在查询中尽可能避免in
和or
,以及嵌入值的硬解析惩罚。 您可以传递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个术语的体系结构限制
有两种解决方法:
- 重构查询以成为连接
- 保持查询不变,但在循环中多次调用它,每次调用使用少于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 }