webentwicklung-frage-antwort-db.com.de

Java Oracle-Ausnahme - "Die maximale Anzahl von Ausdrücken in einer Liste beträgt 1000".

Ich übergebe eine Liste von Strings an meine Abfrage (SQL-Abfrage wurde geschrieben), um die erforderlichen Daten abzurufen. Ich erhalte jedoch diese Ausnahme: 

ora-01795 Die maximale Anzahl von Ausdrücken in einer Liste beträgt 1000

Ich habe überprüft, dass ich mehr als 1000 Einträge in der Liste an den Abfrage-IN-Parameter übergeben habe.

15
Anand

dies ist eine Oracle-Einschränkung für die Anzahl der Listendurchläufe in der Abfrage. 

  1. sie müssen Ihre Abfrage oder hacken
  2. geben Sie stattdessen eine Unterabfrage/einen Join in der IN-Klausel an.
13
digitebs

Sie können keine Liste mit mehr als 1000 Elementen in einer einzigen "Wo" -Bedingung haben, wenn Sie mit Oracle DB arbeiten. So können Sie Ihre "Wo" -Zustand in mehreren "Wo" -Zuständen abschneiden und mit der Klausel "" oder "" verbinden.

Wenn Sie die Kriterien für den Ruhezustand verwenden, können Sie dazu die folgende Java-Methode verwenden. Ersetzen Sie einfach Ihren Code, wo immer Sie ihn verwendet haben 

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

mit 

addCriteriaIn(propertyName, mainList, criteria);

welches die Methode ist: 

 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);
  }
8

Ich habe das Problem gelöst, indem ich die Liste in eine Stapelgröße von 1000 zerlegte und sie mithilfe von OR zusammensetzte.

b. eid [] Array von IDs.

Wenn ich diese Abfrage ausführen möchte,

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

Ich habe diese Abfrage mit einem kleinen Code geschrieben:

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 .... );

Wenn Sie diesen Fehler bei der Verwendung des Ruhezustands angehen, müssen Sie dieses Problem lösen, indem Sie die Liste in Stapel von 100 teilen und dann die einzelnen Ergebnisse verknüpfen (wie in der obigen Abfrage gezeigt). 

Ich glaube nicht, dass dies eine Einschränkung des Ruhezustands ist, wenn dieses Problem nicht behandelt wird, da dieses Problem möglicherweise nicht der Fall einer anderen Datenbank wie MySQL oder DB2 ist. Hibernate ist ein DBM-übergreifendes ORM-Framework.

5
Vallabh Patade

sie können eine temporäre Tabelle erstellen, die gewünschten Werte in Ihre IN-Anweisung einfügen und die temporäre Tabelle mit Ihrer echten Tabelle verbinden. Weitere Informationen zu temporären Tabellen .

3
dursun

Von dba-Oracle.com :

ORA-01795: Die maximale Anzahl von Ausdrücken in einer Liste beträgt 1000 Tipps

Oracle-Fehlertipps von Burleson Consulting (S. Karam)

Die Oracle-Dokumente beachten dies beim ora-01795-Fehler *: ORA-01795 Maximum Anzahl der Ausdrücke in einer Liste ist 1000 Ursache: Mehr als 254 Spalten Oder Ausdrücke wurden in einer Liste angegeben. Maßnahme: Entfernen Sie einige der - Ausdrücke aus der Liste. In den Oracle-MOSC-Foren hat ein Oracle-Benutzer Versucht, den Fehlercode ORA-01795 zu umgehen. Seine Frage wurde von Reem Munakash von Oracle beantwortet:

Das Limit in Oracle8 beträgt 1000 Ausdrücke. Es gibt einen Fehler 495555, der Gegen den Fehlertext eingereicht wurde, der die falsche Nummer angibt (254). Je nach verwendetem Werkzeug kann es jedoch zu einer weiteren Einschränkung Kommen. Die 1000-Ausdrücke liegen innerhalb von sqlplus. 

Die Problemumgehung wäre die Verwendung einer Unterabfrage.

Der Fehler bezüglich der Fehlermeldung wurde in 8.1.5 behoben.

1
Ihor Kostenko

Wenn Sie Ihre db-seitige Logik von einer Abfrage in eine gespeicherte Prozedur konvertieren können, können Sie längere Arrays (Sammlungen) an sie übergeben.

Hier finden Sie ein kurzes Beispiel, wie es geht. Der Link zu den Dokumenten ist veraltet, daher hier ein Link zu den 9i docs 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();

    }
}

und der SQL-Teil

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
bpgergo

Um dieses Problem zu lösen, entschied ich mich, den Hibernate-Core-JAR zu ändern. Ich habe eine Hilfsklasse zum Aufteilen eines Ausdrucks in mehrere Verknüpfungen erstellt: ... t.column IN (: list_1) OR t.column IN (: list_2) ..., Dann habe ich die Methode AbstractQueryImpl.expandParameterList von hibernate geändert, um meine Methode aufzurufen, wenn die Auflistung das Limit überschreitet. 
Meine Hibernate-Core-Version ist 3.6.10.Final, aber es funktioniert gut und für 4.x-Versionen - ich habe es getestet. 
Mein Code wird für die nächsten Fälle getestet:

  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
    );
}

Meine Hilfsklasse InExpressionExpander:

paket org.hibernate.util;

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))
 * </pre>
 * <p/>
 * Example:
 * <pre>
 * 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 + ") ";
    }
}

Ich freue mich über Vorschläge zur Verbesserung dieser Lösung.

0
Vasile Bors