[에러노트] Cause: java.sql.SQLSyntaxErrorException: ORA-01795: 목록에 지정 가능한 식의 최대수는 1000 입니다

생성일:

1 분 소요

현상

데이터가 많은 리스트를 엑셀 다운로드할 때 해당 에러가 발생했다.

원인

해당 에러가 발생한 쿼리를 보니 IN 절에 userOid를 데이터 리스트 갯수만큼 넣고 있었다.

화면에서 검색 조건없이 그대로 엑셀 다운로드를 누르면 총 유저 리스트인 13만건 이상 userOid가 IN절에 ? 로 들어가는 것이다.

즉, 마이바티스에 ?로 들어가는 부분이 1000개를 넘으면 해당 에러가 발생된다.

해결 방법

리마커블 base util 패키지의 ListUtils.*partitionOnSize()를 사용한다.*

public abstract class ListUtils {
        public ListUtils() {
        }

        public static <T> Collection<List<T>> **partitionOnSize**(List<T> inputList, int size) {
                AtomicInteger counter = new AtomicInteger(0);
                return ((Map)inputList.stream().collect(Collectors.groupingBy((input) -> {
                        return counter.getAndIncrement() / size;
                }))).values();
        }
}

사용예시

 List를 size만큼 분리할 경우 사용
 
 List<SyncUserInfo> userList = syncUserTargetDAO.listAll();
 Collection<List<SyncUserInfo>> list = **partitionBasedOnSize**(userList, 1000);

적용

해당 쿼리를 호출하는 commonList

protected List<UserInfo> commonList( List<UserInfo> userList, UserCnd cnd ) {

		if ( CollectionUtils.isEmpty( userList ) ) {
				return CollectionUtils.emptyList();
		}

		List<String> userOids = getUserOidList( userList );

		if ( CollectionUtils.isEmpty( userOids ) ) {
				return CollectionUtils.emptyList();
		}

		... (중략)

		/** 사용자 상태값 채울지 여부 */
		if ( cnd.isFillUserStatus() ) {

			**// 100개 이상인 경우 나눠서 처리
			if ( userOids.size() > SystemConstants.LIST_PARTITION_LIMIT ) {
				Collection<List<String>> partitionOidList = ListUtils.partitionOnSize( userOids, SystemConstants.LIST_PARTITION_LIMIT );
				partitionOidList.parallelStream().forEach( partition -> userStatusBLO.fillUserStatus ( userList, partition ) );
			}**
			else {
				userStatusBLO.fillUserStatus ( userList, userOids );
			}
		}

		return userList;
}
  1. ?표로 들어갈 userOids가 100개 이상인지 (SystemConstants.LIST_PARTITION_LIMIT) 체크한다. 1000개가 아닌 100개인 이유는 성능 테스트 결과 100개로 잘라서 반복했을때 가장 성능이 좋았기 때문이다.
  2. 100개 이상인 경우 ListUtils.partitionOnSize()로 리스트를 분배한다.
  3. 분배된 리스트(partitionOidList)를 돌면서 fillUserStatus()를 호출한다
    1. 분배된 리스트에는 최대 100개의 userOid가 들어있기 때문에 에러가 나지 않는다.
  • 테스트코드

      @Test
      	public void 벌크_셀렉트_for_엑셀_다운로드_테스트 (){
      		ArrayList<String> userTypeList = new ArrayList<>();
      		userTypeList.add( "USSFGG04" );
        
      		ArrayList<String> statusTypeList = new ArrayList<>();
      		statusTypeList.add( "U" );
      		statusTypeList.add( "W" );
        
      		UserCnd cnd =  new UserCnd();
      		cnd.setFillArea( true );
      		cnd.addOrderByList( "U.JOIN_DATE DESC NULLS LAST" );
      		cnd.setUserTypeList( userTypeList );
      		cnd.setStatusTypeList( statusTypeList );
        
      		List<UserInfo> userList =  userBLO.listAll( cnd );
        
      		List<String> userOids = userBLO.getUserOidList( userList );
        
      		System.out.println( "userOids size!! -> " + userOids.size() );
        
      		// userOids가 1000개 이상이면 에러 발생
      		// ListUtils.partitionOnSize()
        
      		if ( userOids.size() > SystemConstants.LIST_PARTITION_LIMIT ) {
        
      			Collection<List<String>> partitionOidList = ListUtils.partitionOnSize( userOids, SystemConstants.LIST_PARTITION_LIMIT );
        
      			System.out.println( "======================= partitionList!!!!!!!!!!!!!!!!!!!!!" );
      			partitionOidList.parallelStream().forEach( data -> System.out.println( data ) );
      			System.out.println( "======================= partitionList!!!!!!!!!!!!!!!!!!!!!" );
        
      			partitionOidList.parallelStream().forEach( partition -> userStatusBLO.fillUserStatus ( userList, partition ) );
      		}
        
      	}
    

관련 용어 및 기능


참고

이게 리마커블 버전인듯?

질문:

Is there a common Java utility to break a list into batches?

https://stackoverflow.com/a/58023258/13037557

댓글남기기