Java Oracle исключение - «максимальное количество выражений в списке - 1000» - PullRequest
16 голосов
/ 19 марта 2012

Я передаю список строк в мой запрос (написанный SQL-запрос), чтобы получить необходимые данные. Но я получаю это исключение:

ora-01795 максимальное количество выражений в списке 1000

Я проверил, что у меня более 1000 записей в списке, переданных параметру запроса IN.

Ответы [ 7 ]

13 голосов
/ 19 марта 2012

это ограничение оракула в количестве проходов по списку в запросе.

  1. вам придется нарезать ваш запрос или
  2. вместо этого укажите подзапрос / объединение в предложении IN.
8 голосов
/ 05 сентября 2013

Вы не можете иметь список с более чем 1000 элементами в одном условии «где», если вы работаете с БД Oracle.Таким образом, вы можете разделить условие «где» в нескольких условиях «где» и соединить их с предложением «или».

Если вы используете hibernate Criteria, вы можете использовать нижеприведенный метод Java для этого.Просто замените ваш код там, где вы когда-либо использовали

criteria.add(Restrictions.in(propertyName, mainList));

на

addCriteriaIn(propertyName, mainList, criteria);

, метод которого:

 private void addCriteriaIn (String propertyName, List<?> list,Criteria criteria)
  {
    Disjunction or = Restrictions.disjunction();
    if(list.size()>1000)
    {        
      while(list.size()>1000)
      {
        List<?> subList = list.subList(0, 1000);
        or.add(Restrictions.in(propertyName, subList));
        list.subList(0, 1000).clear();
      }
    }
    or.add(Restrictions.in(propertyName, list));
    criteria.add(or);
  }
5 голосов
/ 19 декабря 2012

Я решил эту проблему, разбив список на пакеты размером 1000 и соединив его, используя OR.

например, eid [] массив идентификаторов.

Если я хочу выполнить этот запрос,

String sql = select * from employee where some conditions and empid in(eid)

Я переписал этот запрос, написав небольшой фрагмент кода:

String sql = select * from employee where some conditions and ( 
                             empid in(empid[0...999]) OR
                             empid in(empid[1000...1999]) OR
                             empid in(empid[2000...2999]) OR .... );

Чтобы устранить эту ошибку при использовании режима гибернации, необходимо решить эту проблему, разбив списокв пакет из 100, а затем объединить отдельные результаты (как показано в запросе выше).

Я не думаю, что это ограничение hibernate из-за того, что эта проблема не решается, потому что может быть, что эта проблема не относится к другим БД, таким как MySQL или DB2.Hibernate - это интегрированная среда ORM.

4 голосов
/ 19 марта 2012

вы можете создать временную таблицу и вставить значения, которые вы хотите использовать в вашем операторе IN, и соединить временную таблицу с вашей реальной таблицей. дополнительная информация о временных таблицах .

1 голос
/ 19 марта 2012

Если вы можете преобразовать свою логику на стороне базы данных из запроса в хранимую процедуру, вы можете передать ей более длинные массивы (коллекции).

Здесь вы можете найти краткий пример, как это сделать. Ссылка на документы устарела, поэтому вот ссылка на документы 9i http://docs.oracle.com/cd/B10500_01/java.920/a96654/oraarr.htm#1040124

import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;

public class ArrayDemo
{
    public static void passArray() throws SQLException
    {
        Connection conn =
            new OracleDriver().defaultConnection();

        int intArray[] = { 1,2,3,4,5,6 };

        ArrayDescriptor descriptor =
            ArrayDescriptor.createDescriptor( "NUM_ARRAY", conn );

        ARRAY array_to_pass =
            new ARRAY( descriptor, conn, intArray );

        OraclePreparedStatement ps =
            (OraclePreparedStatement)conn.prepareStatement
            ( "begin give_me_an_array(:x); end;" );

        ps.setARRAY( 1, array_to_pass );

        ps.execute();

    }
}

и часть SQL

create or replace type NUM_ARRAY as table of number;

create or replace
procedure give_me_an_array( p_array in num_array )
as
begin
    for i in 1 .. p_array.count
    loop
        dbms_output.put_line( p_array(i) );
    end loop;
end;
1 голос
/ 19 марта 2012

С dba-oracle.com :

ORA-01795: максимальное количество выражений в списке - 1000 советов

Советы по ошибкам Oracle от Burleson Consulting (S. Karam)

Документы Oracle отмечают это при ошибке ora-01795 *: максимум ORA-01795 количество выражений в списке 1000. Причина: более 254 столбцов. или выражения были указаны в списке. Действие: Удалить некоторые из выражения из списка. На форумах Oracle MOSC, пользователь Oracle пытался найти способ обойти код ошибки ORA-01795. Его На вопрос ответил Рим Мунакаш из Oracle:

Ограничение в Oracle8 составляет 1000 выражений. Есть ошибка 495555, подана против текста ошибки, дающего неправильный номер (254). Тем не менее, есть может быть дополнительным ограничением в зависимости от используемого вами инструмента. 1000 выражений в пределах sqlplus.

Временное решение: использовать подзапрос.

Ошибка, связанная с сообщением об ошибке, исправлена ​​в 8.1.5.

0 голосов
/ 07 января 2016

Используя Java Hibernate, для решения этой проблемы я решил сменить JAR с ядром Hibernate.Я сделал вспомогательный класс для разделения выражения на несколько объединений, таких как: ... t.column IN (: list_1) OR t.column IN (: list_2) ..., затем я изменил метод AbstractQueryImpl.expandParameterList с hibernate, чтобы вызывать мой метод, если коллекция превышает ограничение.Моя версия hibernate-core - 3.6.10. Наконец, но она отлично работает и для версий 4.x - я проверил это.Мой код проверен на следующие случаи:

  where t.id in (:idList)
  where (t.id in (:idList))
  where ((t.id) in (:idList))
  where 1=1 and t.id in (:idList)
  where 1=1 and (t.id in (:idList))
  where 1=1 and(t.id) in (:idList)
  where 1=1 and((t.id) in (:idList))
  where 1=1 and(t.id in (:idList))

  where t.id not in (:idList)
  where (t.id not in (:idList))
  where ((t.id) not in (:idList))

AbstractQueryImpl.expandParameterList:

private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
    Collection vals = (Collection) typedList.getValue();
    Type type = typedList.getType();

    boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptor( name ).isJpaStyle();
    String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
    String placeholder =
            new StringBuffer( paramPrefix.length() + name.length() )
                    .append( paramPrefix ).append(  name )
                    .toString();

    if ( query == null ) {
        return query;
    }
    int loc = query.indexOf( placeholder );

    if ( loc < 0 ) {
        return query;
    }

    String beforePlaceholder = query.substring( 0, loc );
    String afterPlaceholder =  query.substring( loc + placeholder.length() );

    // check if placeholder is already immediately enclosed in parentheses
    // (ignoring whitespace)
    boolean isEnclosedInParens =
            StringHelper.getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' &&
                    StringHelper.getFirstNonWhitespaceCharacter( afterPlaceholder ) == ')';

    if ( vals.size() == 1  && isEnclosedInParens ) {
        // short-circuit for performance when only 1 value and the
        // placeholder is already enclosed in parentheses...
        namedParamsCopy.put( name, new TypedValue( type, vals.iterator().next(), session.getEntityMode() ) );
        return query;
    }

    // *** changes by Vasile Bors for HHH-1123 ***
    // case vals.size() > 1000
    if ((vals.size() >= InExpressionExpander.MAX_ALLOWED_PER_INEXPR) && isEnclosedInParens) {

        InExpressionExpander inExpressionExpander = new InExpressionExpander(beforePlaceholder, afterPlaceholder);
        if(inExpressionExpander.isValidInOrNotInExpression()){

            List<String> list = new ArrayList<String>( vals.size() );
            Iterator iter = vals.iterator();
            int i = 0;
            String alias;
            while ( iter.hasNext() ) {
                alias = ( isJpaPositionalParam ? 'x' + name : name ) + i++ + '_';
                namedParamsCopy.put( alias, new TypedValue( type, iter.next(), session.getEntityMode() ) );
                list.add(ParserHelper.HQL_VARIABLE_PREFIX + alias );
            }

            String expandedExpression = inExpressionExpander.expandExpression(list);
            if(expandedExpression != null){
                return expandedExpression;
            }
        }
    }
    // *** end changes by Vasile Bors for HHH-1123 ***

    StringBuffer list = new StringBuffer(16);
    Iterator iter = vals.iterator();
    int i = 0;
    while (iter.hasNext()) {
        String alias = (isJpaPositionalParam ? 'x' + name : name) + i++ + '_';
        namedParamsCopy.put(alias, new TypedValue(type, iter.next(), session.getEntityMode()));
        list.append(ParserHelper.HQL_VARIABLE_PREFIX).append(alias);
        if (iter.hasNext()) {
            list.append(", ");
        }
    }

    return StringHelper.replace(
            beforePlaceholder,
            afterPlaceholder,
            placeholder.toString(),
            list.toString(),
            true,
            true
    );
}

Мой вспомогательный класс InExpressionExpander:

package org.hibernate.util;

<code>import org.hibernate.QueryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.List;
import java.util.Stack;

/**
 * Utility class for expand Hql and Sql IN expressions with a parameter with more than IN expression limit size (HHH-1123).
 * <br/>
 * It work for expression with formats:
 * <pre>
 *
 * where t.id in (:idList)
 * where (t.id in (:idList))
 * where ((t.id) in (:idList))
 * where 1=1 and t.id in (:idList)
 * where 1=1 and (t.id in (:idList))
 * where 1=1 and(t.id) in (:idList)
 * where 1=1 and((t.id) in (:idList))
 * where 1=1 and(t.id in (:idList))
 *
 * where t.id not in (:idList)
 * where (t.id not in (:idList))
 * where ((t.id) not in (:idList))
 * 
** Пример: *
 * select t.id from tableOrEntity t where t.id IN (:idList)
 * </pre
 *
 * @author Vasile Bors
 * @since 13/12/2015.
 */
public class InExpressionExpander {
    private static final Logger log = LoggerFactory.getLogger(InExpressionExpander.class);

    public static final  int MAX_ALLOWED_PER_INEXPR = 1000;
    private static final int MAX_PARAMS_PER_INEXPR  = 500;

    private Stack<String> stackExpr = new Stack<String>();
    private StringBuilder toWalkQuery;

    private final String beforePlaceholder;
    private final String afterPlaceholder;
    private boolean wasChecked         = false;
    private boolean isEnclosedInParens = false;
    private boolean isInExpr           = false;
    private boolean isNotInExpr        = false;

    public InExpressionExpander(String beforePlaceholder, String afterPlaceholder) {
        this.toWalkQuery = new StringBuilder(beforePlaceholder);

        this.beforePlaceholder = beforePlaceholder;
        this.afterPlaceholder = afterPlaceholder;
    }

    public boolean isValidInOrNotInExpression() {
        if (!wasChecked) {
            String lastExpr = extractLastExpression();
            if ("(".equals(lastExpr)) {
                isEnclosedInParens = true;
                lastExpr = extractLastExpression();
            }
            isInExpr = "in".equalsIgnoreCase(lastExpr);
        }

        wasChecked = true;
        return isInExpr;
    }

    public String expandExpression(List paramList) {
        if (isValidInOrNotInExpression()) {

            final String lastExpr = extractLastExpression(false);

            if ("not".equalsIgnoreCase(lastExpr)) {
                isNotInExpr = true;
                extractLastExpression(); //extract "not" and consume it
            }

            extractColumnForInExpression();

            StringBuilder exprPrefixBuilder = new StringBuilder();
            for (int i = stackExpr.size() - 1; i > -1; i--) {
                exprPrefixBuilder.append(stackExpr.get(i)).append(' ');
            }
            if (!isEnclosedInParens) {
                exprPrefixBuilder.append('(');
            }

            String expandedExpression = expandInExpression(exprPrefixBuilder, paramList);
            String beforeExpression = getBeforeExpression();
            String afterExpression = getAfterExpression();

            String expandedQuery = new StringBuilder(beforeExpression).append(expandedExpression)
                    .append(afterExpression)
                    .toString();

            if (log.isDebugEnabled()) {
                log.debug(
                        "Query was changed to prevent exception for maximum number of expression in a list. Expanded IN expression query:\n {}",
                        expandedExpression);

                log.debug("Expanded query:\n {}", expandedQuery);
            }

            return expandedQuery;
        }

        log.error("Illegal call of InExpressionExpander.expandExpression() without IN expression.");
        return null;
    }

    private String expandInExpression(StringBuilder exprPrefixBuilder, List values) {

        String joinExpr = isNotInExpr ? ") and " : ") or ";
        StringBuilder expr = new StringBuilder(16);
        Iterator iter = values.iterator();
        int i = 0;
        boolean firstExpr = true;
        while (iter.hasNext()) {
            if (firstExpr || i % MAX_PARAMS_PER_INEXPR == 0) {
                //close previous expression and start new expression
                if (!firstExpr) {
                    expr.append(joinExpr);
                } else {
                    firstExpr = false;
                }
                expr.append(exprPrefixBuilder);
            } else {
                expr.append(", ");
            }
            expr.append(iter.next());
            i++;
        }

        expr.append(')');// close for last in expression

        return expr.toString();
    }

    /**
     * Method extract last expression parsed by space from toWalkQuery and remove it from toWalkQuery;<br/>
     * If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
     *
     * @return last expression from toWalkQuery
     */
    private String extractLastExpression() {
        return extractLastExpression(true);
    }

    /**
     * Method extract last expression parsed by space from toWalkQuery, remove it from toWalkQuery if is consume = true;<br/>
     * If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
     *
     * @param consum if true  the method will extract and remove last expression from toWalkQuery
     * @return last expression from toWalkQuery
     */
    private String extractLastExpression(final boolean consum) {
        int lastIndex = this.toWalkQuery.length() - 1;
        String lastExpr;
        int exprSeparatorIndex = this.toWalkQuery.lastIndexOf(" ");
        if (lastIndex == exprSeparatorIndex) { //remove last space from the end
            this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
            return extractLastExpression(consum);
        } else {
            lastExpr = this.toWalkQuery.substring(exprSeparatorIndex + 1, this.toWalkQuery.length());

            if (lastExpr.length() > 1) {
                if (lastExpr.endsWith(")")) {
                    //if parens are closed at the end we need to find where it is open
                    int opensParens = 0;
                    int closedParens = 0;
                    int startExprIndex = -1;

                    char c;
                    for (int i = lastExpr.length() - 1; i > -1; i--) {
                        c = lastExpr.charAt(i);
                        if (c == ')') {
                            closedParens++;
                        } else if (c == '(') {
                            opensParens++;
                        }

                        if (closedParens == opensParens) {
                            startExprIndex = i;
                            break;
                        }
                    }

                    if (startExprIndex > -1) {
                        lastExpr = lastExpr.substring(startExprIndex, lastExpr.length());
                        exprSeparatorIndex = exprSeparatorIndex + startExprIndex
                                + 1; // +1 because separator is not space and don't must be deleted
                    }
                } else if (lastExpr.contains("(")) {
                    int parentsIndex = exprSeparatorIndex + lastExpr.indexOf('(') + 1;
                    this.toWalkQuery.replace(parentsIndex, parentsIndex + 1, " ( ");
                    return extractLastExpression(consum);
                }
            }

            if (consum) {
                this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
            }
        }

        if (consum) {
            stackExpr.push(lastExpr);
        }

        return lastExpr;
    }

    private String extractColumnForInExpression() {
        String column = extractLastExpression();

        String beforeColumn = extractLastExpression(false);
        long pointIndx = beforeColumn.lastIndexOf('.');
        if (pointIndx > -1) {
            if (pointIndx == (beforeColumn.length() - 1)) {
                throw new QueryException(
                        "Invalid column format: " + beforeColumn + ' ' + column
                                + " . Remove space from column!");
            }
        }
        return column;
    }

    private String getBeforeExpression() {
        return this.toWalkQuery + " (";
    }

    private String getAfterExpression() {
        if (StringHelper.getFirstNonWhitespaceCharacter(afterPlaceholder) == ')') {
            return afterPlaceholder;
        }
        return afterPlaceholder + ") ";
    }
}
</code>

Я рад получить любые предложения по улучшению этого решения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...