在IN子句中执行超过1000条记录时,我的Java代码出现ORA-01795
错误。
我想在1000个条目的批处理中使用由OR子句分隔的多个IN子句来打破它,如下所示:
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条记录等条件的逻辑。谁能在这帮助我?
答案 0 :(得分:3)
围绕此限制有三种可能的方式:
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的限制。我不确定Oracle会解析这个问题的速度有多快。
3)将值插入(全局)临时表,然后使用IN
子句(或JOIN
)。这可能是最快的解决方案。
答案 1 :(得分:1)
我最近自己打了这堵墙:
Oracle在IN()
有两种解决方法:
选项1取决于具体情况。如果您的值列表来自查询,则可以重构为连接
选项2也很简单,但性能较差:
List<String> terms;
for (int i = 0; i <= terms.size() / 1000; i++) {
List<String> next1000 = terms.subList(i * 1000, Math.min((i + 1) * 1000, terms.size());
// build and execute query using next1000 instead of terms
}
答案 2 :(得分:1)
如果有这么多的值,我会避免in
和or
以及嵌入值的硬解析惩罚,如果可能的话,在查询中。您可以传递一组SQL值集合,并使用table()
集合表达式作为表格,您可以将真实表格加入。
这使用硬编码的整数数组作为示例,但您可以从用户输入填充该数组。我正在使用built-in collection type definitions,例如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的内存可以处理的数量一样多)并且不受in
列表的1000个成员限制的约束。< / p>
基本上table(?)
最终看起来像一个包含所有值的表,这比使用所有值填充真实或临时表并加入其中更容易,更快。
当然,不要真正使用t.*
,列出你需要的列;我假设您使用*
来解释问题......
(Here is a more complete example,但情节略有不同。)
答案 3 :(得分:0)
在这种情况下,当我在Java中使用List
中的id时,我使用这样的实用程序类将列表拆分为分区并从这些分区生成语句:
public class ListUtils {
public static <T> List<List<T>> partition(List<T> 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<List<T>> 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)
然后你可以迭代结果来构造最终的查询字符串。