2012-03-19 96 views
15

Estoy pasando una lista de cadenas a mi consulta (consulta SQL escrita) para obtener los datos requeridos. Pero me estoy haciendo esta excepción:Excepción de Oracle de Java: "el número máximo de expresiones en una lista es 1000"

ora-01795 número máximo de expresiones en una lista es 1000

he comprobado que tengo más de 1000 entradas en la lista aprobada para la consulta en el parámetro .

+0

proporcionando código y consulta puede ayudar. – Zohaib

+3

"Comprobé que tengo más de 1000 entradas en la lista que se pasa al parámetro IN de la consulta". - y cuál fue el resultado de este control? – skaffman

+0

@skaffman, supuse que eso significaba que había más de 1,000 en la lista. Y si está buscando ayuda para solucionarlo, la única forma de hacerlo es mediante la consulta que está ejecutando. Un signo de interrogación también podría ser útil. – Ben

Respuesta

12

esto es una limitación oráculo en el número de paso de lista en la consulta.

  1. tendrá que picar su consulta o
  2. proporcionan una subconsulta/unirse a la cláusula IN en su lugar.
1

De dba-oracle.com:

ORA-01795: número máximo de expresiones en una lista es de 1000 consejos

Oracle error Consejos de Burleson Consulting (S. Karam)

El Oracle nota esto en el error ora-01795 *: ORA-01795 máximo número de expresiones en una lista es 1000 Causa: Más de 254 columnas o expresiones se especificaron en una lista. Acción: Retire algunos de los expresiones de la lista. En los foros de Oracle Mosc, un usuario de Oracle estaba tratando de encontrar una forma de evitar código de error ORA-01795. Su pregunta fue respondida por Reem Munakash de Oracle:

El límite en Oracle8 es de 1000 expresiones. Hay un error 495.555, presentada contra el texto de error que indica el número equivocado (254). Sin embargo, hay puede haber una restricción adicional dependiendo de la herramienta que está utilizando. Las expresiones 1000 están dentro de sqlplus.

La solución sería utilizar una subconsulta.

El error en relación el mensaje de error se fija en el punto 8.1.5.

1

Si puede convertir su lógica de lado db de una consulta en un procedimiento almacenado, puede pasar matrices (colecciones) más largas.

Here puede encontrar un breve ejemplo de cómo hacerlo. El enlace a la documentación no está actualizado, por lo que aquí hay un enlace a la documentación 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(); 

    } 
} 

y el SQL parte

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; 
5

Resolví esto al dividir la lista en lote de tamaño 1000 y unirlo usando OR.

p. Ej. eid [] matriz de ids.

Si quiero para ejecutar esta consulta,

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

he reescrito esta consulta escribiendo un pequeño trozo de código:

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

Tratar con este error durante el uso de hibernación, se tiene que abordar este problema cortando la lista en un lote de 100 y luego unir los resultados individuales (como se muestra en la consulta anterior).

No creo que sea una limitación de hibernación por no manejar este problema, porque puede ser que este problema no sea el caso de otro DB como MySQL o DB2. Hibernate es un marco de ORM entre bases de datos.

+1

La expresión O tiene un rendimiento muy pobre aquí, especialmente si tiene un par de miles de elementos.Sub-consulta o tabla temporal funcionan mejor – Hawk

8

No puede tener una lista con más de 1000 elementos en una sola condición "donde" si está trabajando con Oracle DB. Entonces puede cortar su condición de "dónde" en múltiples condiciones de "dónde" y unirlas con la cláusula "o".

Si está utilizando criterios de hibernación, puede utilizar el siguiente método de Java para hacerlo. Basta con sustituir el código donde quiera que utilizó

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

con

addCriteriaIn(propertyName, mainList, criteria); 

el que el método es:

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

Gracias por ese útil método. – AHungerArtist

+0

Gracias, eso es útil. Una sugerencia menor debería ser no alterar la lista original, ya que podría ser usada fuera de este método. Por ejemplo, manejando un índice de comienzo/final. y aumentarlos en trozos de +1000. – jmendiola

0

Uso de Java Hibernate, para resolver este problema, decidí cambiar la hibernación -core JAR. Hice una clase de ayuda para dividir una expresión en más uniones como: ... t.column IN (: list_1) OR t.column IN (: list_2) ..., luego cambié el método AbstractQueryImpl.expandParameterList de hibernate para llamar a mi método si la colección excede el límite.
Mi versión hibernate-core es 3.6.10.Final, pero funciona bien y para versiones 4.x - Lo probé.
Mi código es probado para los próximos casos:

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

Mi clase de ayuda InExpressionExpander:

paquete 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 + ") "; 
    } 
} 

Me complace recibir sugerencias para mejorar esta solución.

Cuestiones relacionadas