2012-03-19 29 views
15

Tôi đang chuyển danh sách các Chuỗi đến truy vấn của tôi (truy vấn SQL được viết) để tìm nạp dữ liệu cần thiết. Nhưng tôi nhận được ngoại lệ này:Ngoại lệ Java Oracle - "số lượng biểu thức tối đa trong danh sách là 1000"

ora-01.795 số lượng tối đa của biểu thức trong danh sách là 1000

Tôi đã kiểm tra mà tôi có nhiều hơn 1000 mục trong danh sách truyền cho các truy vấn trong tham số .

+0

cung cấp mã và truy vấn có thể hữu ích. – Zohaib

+3

"Tôi đã kiểm tra rằng tôi có hơn 1000 mục nhập trong danh sách được chuyển đến tham số truy vấn IN". - Và kết quả của việc kiểm tra này là gì? – skaffman

+0

@skaffman, tôi cho rằng điều đó có nghĩa là có _are_ hơn 1.000 trong danh sách. anand, nếu bạn đang tìm kiếm sự trợ giúp để sửa lỗi này thì truy vấn bạn đang chạy là cách duy nhất. Một dấu hỏi cũng có thể hữu ích. – Ben

Trả lời

12

đây là giới hạn oracle về số lượng danh sách vượt qua trong truy vấn.

  1. bạn sẽ phải chặt truy vấn của bạn hoặc
  2. cung cấp một subquery/join trong mệnh đề TRÊN thay thế.
1

Từ dba-oracle.com:

ORA-01.795: số lượng tối đa của biểu thức trong danh sách là 1000 lời khuyên

Lỗi Oracle Mẹo bởi Burleson Consulting (S. Karam)

Các Oracle tài liệu lưu ý điều này trên lỗi ora-01795 *: ORA-01795 tối đa số biểu thức trong danh sách là 1000 Nguyên nhân: Hơn 254 cột hoặc biểu thức được chỉ định trong danh sách. Hành động: Xóa một số biểu thức khỏi danh sách. Trong Diễn đàn MOSC của Oracle, một người dùng Oracle đang cố gắng tìm cách xung quanh mã lỗi ORA-01795. Câu hỏi của ông đã được trả lời bởi Reem Munakash of Oracle:

Giới hạn trong Oracle8 là 1000 biểu thức. Có lỗi 495555, được gửi đối với văn bản lỗi cung cấp số sai (254). Tuy nhiên, có có thể là một hạn chế khác tùy thuộc vào công cụ bạn đang sử dụng. Các biểu thức 1000 nằm trong sqlplus.

Giải pháp thay thế sẽ là sử dụng truy vấn phụ.

Lỗi liên quan đến thông báo lỗi được sửa trong 8.1.5.

1

Nếu bạn có thể chuyển đổi logic bên db của bạn từ truy vấn thành thủ tục được lưu trữ, bạn có thể truyền mảng dài hơn (bộ sưu tập) cho nó.

Here bạn có thể tìm thấy ví dụ ngắn gọn về cách thực hiện. Các liên kết đến các tài liệu là lỗi thời, vì vậy đây là một liên kết đến các tài liệu 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(); 

    } 
} 

và SQL phần

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

bạn có thể tạo một bảng tạm thời, và chèn các giá trị bạn muốn sử dụng trong câu hỏi IN của bạn và tham gia bảng tạm thời với bảng thực của bạn. more information about temporary tables.

5

Tôi đã giải quyết điều này bằng cách chia nhỏ danh sách thành hàng loạt kích thước 1000 và tham gia bằng cách sử dụng OR.

ví dụ: eid [] mảng id.

Nếu tôi muốn thực hiện truy vấn này,

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

Tôi đã viết lại truy vấn này bằng cách viết một đoạn mã nhỏ:

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

Đối phó với lỗi này trong khi sử dụng chế độ ngủ đông, bạn phải giải quyết vấn đề này bằng cách cắt danh sách thành hàng loạt 100 và sau đó tham gia các kết quả riêng lẻ (như được hiển thị trong truy vấn ở trên).

Tôi không nghĩ rằng đó là một hạn chế của ngủ đông vì không xử lý vấn đề này, bởi vì nó có thể là trường hợp vấn đề này không phải là trường hợp của một DB khác như MySQL hay DB2. Hibernate là một khuôn khổ DB DB chéo.

+1

Biểu hiện HOẶC có hiệu suất rất kém ở đây, đặc biệt nếu bạn có một vài nghìn mặt hàng.Truy vấn phụ hoặc bảng tạm thời hoạt động tốt hơn – Hawk

8

Bạn không thể có danh sách có hơn 1000 thành phần trong một điều kiện "ở đâu" nếu bạn đang làm việc với Oracle DB. Vì vậy, bạn có thể cắt xuống điều kiện "ở đâu" của bạn trong nhiều điều kiện "ở đâu" và kết hợp chúng với mệnh đề "hoặc".

Nếu bạn đang sử dụng Tiêu chuẩn ngủ đông, bạn có thể sử dụng phương pháp Java bên dưới để thực hiện việc này. Chỉ cần thay thế mã của bạn có bao giờ bạn sử dụng

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

với

addCriteriaIn(propertyName, mainList, criteria); 

mà phương pháp này là:

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

Cảm ơn phương pháp hữu ích đó. – AHungerArtist

+0

Cảm ơn, điều đó rất hữu ích. Một gợi ý nhỏ không nên thay đổi danh sách ban đầu, vì nó có thể được sử dụng bên ngoài phương pháp này. Ví dụ, xử lý một chỉ mục bắt đầu/kết thúc. và tăng chúng theo khối +1000. – jmendiola

0

Sử dụng Java Hibernate, để giải quyết vấn đề này, tôi quyết định thay đổi Hibernate -JAR lõi. Tôi madea một lớp helper để phân chia một biểu thức trong nhiều gia nhập như: ... t.column IN (: list_1) OR t.column IN (: list_2) ..., Sau đó tôi đã thay đổi phương thức AbstractQueryImpl.expandParameterList từ hibernate để gọi phương thức của tôi nếu bộ sưu tập vượt quá giới hạn.
Phiên bản lõi ngủ đông của tôi là 3.6.10.Final, nhưng nó hoạt động tốt và cho các phiên bản 4.x - tôi đã thử nghiệm nó.
Mã của tôi được kiểm tra đối với trường hợp tiếp theo:

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

My lớp helper InExpressionExpander:

gói 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 + ") "; 
    } 
} 

Tôi rất vui khi nhận được bất kỳ đề xuất nào để cải thiện giải pháp này.

Các vấn đề liên quan