2012-03-19 15 views
15

Przekazuję listę ciągów do mojego zapytania (napisano zapytanie SQL), aby pobrać wymagane dane. Ale otrzymuję ten wyjątek:Wyjątek Java Oracle - "maksymalna liczba wyrażeń na liście to 1000"

ORA-01795 Maksymalna liczba wyrażeń w liście jest 1000

sprawdziłem, że mam ponad 1000 wpisów w wykazie przekazanym do zapytania w parametrze .

+0

podanie kodu i zapytania może pomóc. – Zohaib

+3

"Sprawdziłem, czy mam więcej niż 1000 wpisów na liście przekazanych do parametru zapytania IN." - i jaki był wynik tego czeku? – skaffman

+0

@skaffman, założyłem, że oznacza to, że na liście jest więcej niż 1000. Jeśli szukasz pomocy w rozwiązaniu tego problemu, to zapytanie jest jedynym sposobem. Pomocny może być także znak zapytania. – Ben

Odpowiedz

12

jest to ograniczenie liczby wyrażeń w liście w zapytaniu.

  1. trzeba będzie posiekać zapytanie lub
  2. zapewnić podkwerenda/join w klauzuli w zamian.
1

Od dba-oracle.com:

ORA-01795: Maksymalna liczba wyrażeń na liście znajduje się 1000 porad

Oracle Porady błąd, Burleson Consulting (S. Karam)

Wyrocznia docs zauważ to na poziomie błędu ora-01795 *: ORA-01795 maksymalnie liczba wyrażeń na liście to 1000 Przyczyna: Na liście podano więcej niż 254 kolumny lub wyrażeń. Działanie: Usuń niektóre z wyrażeń z listy. Na forach Oracle MOSC użytkownik Oracle próbował znaleźć sposób obejścia kodu błędu ORA-01795. Odpowiedzi na jego pytanie: odpowiedział Reem Munakash z Oracle:

Limit w Oracle8 to 1000 wyrażeń. Istnieje błąd 495555, zgłoszony przeciwko tekstowi błędu podając zły numer (254). Jednak w zależności od używanego narzędzia może występować dodatkowe ograniczenie w zakresie . Wyrażenia 1000 są w ciągu sqlplus.

Obejście problemu polegałoby na użyciu pod-zapytania.

Błąd związany z komunikatem o błędzie został naprawiony w wersji 8.1.5.

1

Jeśli możesz przekonwertować swoją logikę strony DB z zapytania do procedury składowanej, możesz przekazać do niej dłuższe tablice (kolekcje).

Here można znaleźć krótki przykład jak to zrobić. Link do docs jest przestarzała, więc tutaj jest link do 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(); 

    } 
} 

i SQL część

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

Rozwiązałem to, dzieląc listę na partię wielkości 1000 i dołączając do niej za pomocą OR.

np. eid [] tablica identyfikatorów.

Jeśli chcę wykonać to zapytanie,

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

mam ponownie napisany tej kwerendy pisząc kawałek kodu:

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

czynienia z tym błędem podczas korzystania z hibernacji, Muszą rozwiązać ten problem, przerywając listę na 100 sztuk, a następnie łącząc poszczególne wyniki (jak pokazano w powyższym zapytaniu).

Nie uważam, że jest to ograniczenie hibernacji, ponieważ nie radzimy sobie z tym problemem, ponieważ może się zdarzyć, że problem ten nie dotyczy innego DB, takiego jak MySQL lub DB2. Hibernate jest strukturą ORM opartą na wielu bazach danych.

+1

OR wyrażenie ma tutaj bardzo słabe wyniki, szczególnie jeśli masz kilka tysięcy przedmiotów.Sub-zapytanie lub tabela tymczasowa działają lepiej – Hawk

8

Nie możesz mieć listy zawierającej więcej niż 1000 elementów w jednym warunku "where", jeśli pracujesz z Oracle DB. Możesz więc wyciąć warunki "gdzie" w wielu warunkach "gdzie" i połączyć je z klauzulą ​​"lub".

Jeśli używasz Kryteriów hibernacji, możesz użyć poniższej metody Java, aby to zrobić. Wystarczy zastąpić kod gdzie kiedykolwiek użyłeś

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

z

addCriteriaIn(propertyName, mainList, criteria); 

która metoda jest:

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

Dzięki za pomocną metodę. – AHungerArtist

+0

Dzięki, to jest przydatne. Niewielką sugestią nie powinno być zmienianie oryginalnej listy, ponieważ może ona być używana poza tą metodą. Na przykład obsługa indeksu początku/końca. i zwiększyć je o kawałki +1000. – jmendiola

0

Korzystanie Java Hibernate, aby rozwiązać ten problem, postanowiłem zmienić Hibernate - JAR. Zrobiłem klasę pomocniczą, aby podzielić wyrażenie w więcej złączeń, takich jak: ... t.column IN (: list_1) OR t.column IN (: list_2) ..., Potem zmieniłem metodę AbstractQueryImpl.expandParameterList ze stanu hibernacji, aby wywoływać moją metodę, jeśli kolekcja przekroczyła limit.
Moja wersja hibernate-core to 3.6.10.Final, ale działa dobrze i dla wersji 4.x - testowałem to.
Mój kod jest testowany przez następnych przypadkach:

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

Moja klasa pomocnika InExpressionExpander:

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

Cieszę się, że mogę otrzymać sugestie dotyczące ulepszenia tego rozwiązania.

Powiązane problemy