Oracle数据库中IN参数个数超过1000的问题

    在oracle中,我们使用in方法查询记录的时候,如果in后面的参数个数超过1000个,那么会发生错误,JDBC会抛出“java.sql.SQLException: ORA-01795: 列表中的最大表达式数为 1000”这个异常。初步解决这个问题的思想是把参数列表分段,将SQL语句拼成“or XX in(.....) or XX in(.....)”形式。我写的QueryUtil类分别给出了SQL、HQL和Hibernate中解决这个问题的方法:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

public class QueryUtil {
	/**
	 * 解决Oracle中in的参数列表长度超过1000的问题
	 * 
	 * @param length
	 *            每个分段的长度,
	 * @param paramsList
	 *            待拆分的参数列表
	 * @return 拆分后的分段列表
	 */
	public static <T> List<List<T>> splitInParams(int length, List<T> paramsList) {
		if (length<1||paramsList == null || paramsList.size() == 0)
			return null;
		int size = paramsList.size();
		List<List<T>> list = new ArrayList<List<T>>();
		int d = (int) Math.ceil(size / (length+0.0));
		for (int i = 0; i < d; i++) {
			int fromIndex = length * i;
			int toIndex = Math.min(fromIndex + length, size);
			list.add(paramsList.subList(fromIndex, toIndex));
		}
		return list;
	}
	
	
	/**
	 * 解决Oracle中in的参数列表长度超过1000的问题
	 * 返回拼接的SQL语句
	 * eg:
	 * 返回的SQL语句片段:XX IN (.......) OR XX IN (......)这样的形式
	 * @param paramName  需要进行in查询的查询参数的名称
	 * @param length     每个分段的长度,对于Oracle,一般设置为800-1000
	 * @param paramsList 待拆分的参数列表
	 * @return 拼接的SQL语句片段
	 */
	public static <T> String getSQLInParamsSplit(String paramName,int length,List<T> paramsList){
		if(length<1||paramName==null||paramsList==null||paramsList.size()==0)
			return null;
		List<List<T>> list = splitInParams(length, paramsList);
		StringBuilder sb = new StringBuilder();
		String temp = list.get(0).toString();
		//由于List的toString方法返回的是[....]形式,需要去掉开头和结尾的中括号
		sb.append(paramName).append(" IN ("+temp.subSequence(1,	temp.length()-1)+") ");
		int size = list.size();
		for(int i=1;i<size;i++){
			temp = list.get(i).toString();
			sb.append(" OR "+paramName+" IN ("+temp.subSequence(1,	temp.length()-1)+") ");
		}
		return sb.toString();
	}
	
	
	
	/**
	 * 解决Oracle中in的参数列表长度超过1000的问题
	 * 返回命名参数查询的HQL语句片段和对应的参数名和值的Map
	 * eg:
	 * 返回的HQL语句片段:id IN (:id0) OR id IN (:id1) OR id IN (:id2) OR id IN (:id3),命名参数的名称是以paramName加上序号
	 * @param paramName  需要进行in查询的查询参数的名称
	 * @param length     每个分段的长度,对于Oracle,一般设置为800-1000
	 * @param paramsList 待拆分的参数列表
	 * @return 返回一个长度为2的Object数组,
	 * 第一个元素是还有in的HQL语句片段,
	 * 第二个元素是Map<String,Object>,其中String是参数名,Object是参数值的列表
	 */
	public static <T> Object[] getHQLInParamsSplit(String paramName,int length,List<T> paramsList){
		if(length<1||paramName==null||paramsList==null||paramsList.size()==0)
			return null;
		List<List<T>> list = splitInParams(length, paramsList);
	    Map<String, Object> tempParamsValues = new HashMap<String, Object>();
		StringBuilder sb = new StringBuilder();
		String tempParamName = paramName+0;
		sb.append(paramName).append(" IN (:").append(tempParamName).append(")");
		tempParamsValues.put(tempParamName, list.get(0));
		int size = list.size();
		for(int i=1;i<size;i++){
			tempParamName = paramName+i; 
			sb.append(" OR ").append(paramName).append(" IN (:").append(tempParamName).append(")");
			tempParamsValues.put(tempParamName, list.get(i));	
		}
		Object[] hqlAndParamsMap = new Object[2];
		hqlAndParamsMap[0]=sb.toString();
		hqlAndParamsMap[1]=tempParamsValues;
		return hqlAndParamsMap;
	}
	
	

	
	/**
	 * 解决Oracle中in的参数列表长度超过1000的问题
	 * 获取拆分in参数列表后的Criterion
	 * @param paramName  需要进行in查询的查询参数的名称
	 * @param length     每个分段的长度,对于Oracle,一般设置为800-1000
	 * @param paramsList 待拆分的参数列表
	 * @return 含有嵌套or的Criterion
	 */
	public static <T> Criterion getCriterionInParamsSplit(String paramName,int length,List<T> paramsList){
		if(length<1||paramName==null||paramsList==null||paramsList.size()==0)
			return null;
		List<List<T>> list = splitInParams(length, paramsList);
		Criterion criterion = Restrictions.in(paramName, list.get(0));
		int size = list.size();
		for(int i=1;i<size;i++)
			criterion=Restrictions.or(criterion, Restrictions.in(paramName, list.get(i)));
		return criterion;
	}
	
}

    上面可以初步解决问题,为什么只是初步呢?因为in后面的参数个数很多时会导致数据库执行时间过长,而导致连接超时,Hibernate中会抛出“java.sql.SQLException: Io 异常: Connection reset by peer: socket write error"异常,而直接用JDBC写会抛出“java.sql.SQLRecoverableException: 无法从套接字读取更多的数据”。下面列出一条简单的SQL语句“SELECT count(*)from JBPM4_EXT_TASK_ENGINE_ where 1=1”后面拼接“ or 1=1 ”时JDBC执行时间:

 

 SQL语句长度(字符数)JDBC执行时间(毫秒)最后取到的记录数(条)
第一组数据165366051192
第二组数据240531239151192
第三组数据6405316138251192


   由此可见SQL语句过长导致JDBC执行时间的增长率是很快的,当SQL语句中的字符数达到某一个数量时肯定会导致连接超时,当然我没有去测这个阈值。不过,产生这个现象的原因是什么,我还不是很清楚(可能是字符数,也可能是查询条件过多而导致表达式树过于庞大而超出数据库的处理能力)。

   那么如何解决这个问题呢?方法一般有两种:1.用表关联代替IN;2.在where条件中使用子查询,如“select  * from b where c in (select  d from e ......)”

这样的形式。

   总而言之,在SQL语句中要慎用IN,IN一般只用于参数个数较少的情况。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值