【问题标题】:Is there a workaround for ORA-01795: maximum number of expressions in a list is 1000 error?ORA-01795 是否有解决方法:列表中的最大表达式数为 1000 错误?
【发布时间】:2013-07-24 10:15:18
【问题描述】:

是否有解决方法

'ORA-01795: maximum number of expressions in a list is 1000 error'

我有一个查询,它根据一个字段的值选择字段。我正在使用 in 子句,并且有 10000 多个值

示例:

select field1, field2, field3 
from table1 
where name in 
(
'value1',
'value2',
...
'value10000+'
);

每次执行查询时,我都会得到ORA-01795: maximum number of expressions in a list is 1000 error。我正在尝试在 TOAD 中执行查询,没有区别,同样的错误。我将如何修改查询以使其正常工作?

提前致谢

【问题讨论】:

  • 将 value1..value1000+ 放入表中并选择名称(从表中选择值)
  • 错误不依赖于执行查询的环境(例如 SQL*Plus 或 TOAD 或 ...)。

标签: sql toad


【解决方案1】:

使用元组:

假设输入是:

List<Long> userIdList = Arrays.asList(100L,200L,300L);

StringBuilder tuple = new StringBuilder();
for(Long userId : userIdList) {
   tuple.append("(1,").append(userId).append("),");
}
tuple.deleteCharAt(tuple.length()-1);

输出将是: (1,100),(1,200),(1,300)

我们可以像这样将它传递给下面的查询(是的,我们可以传递超过 1000 个元素):

SELECT * FROM MyTable WHERE (1, USR_ID) IN ((1,100),(1,200),(1,300));

【讨论】:

    【解决方案2】:

    通过列表和循环中需要返回的记录数大多数情况= 999。

    List<List<Long>> getSubLists = batchList(inputList, 999);
    List<Long> newList = new ArrayList<>();
    for (List<Long> subSet : getSubLists) { newList.addALL(daoCall) // add in the required list in loop }
    
     public static <T> List<List<T>> batchList(List<T> inputList, final int maxSize) {
        List<List<T>> sublists = new ArrayList<>();
        final int size = inputList.size();
        for (int i = 0; i < size; i += maxSize) {
            sublists.add(new ArrayList<>(inputList.subList(i, Math.min(size, i + maxSize))));
        }
        return sublists;
    }
    
    
    
    
    
    
    
    
      
    

    【讨论】:

      【解决方案3】:
          **Divide a list to lists of n size**
      
          import java.util.AbstractList;
          import java.util.ArrayList;
          import java.util.List;
      
          public final class PartitionUtil<T> extends AbstractList<List<T>> {
      
              private final List<T> list;
              private final int chunkSize;
      
              private PartitionUtil(List<T> list, int chunkSize) {
                  this.list = new ArrayList<>(list);
                  this.chunkSize = chunkSize;
              }
      
              public static <T> PartitionUtil<T> ofSize(List<T> list, int chunkSize) {
                  return new PartitionUtil<>(list, chunkSize);
              }
      
              @Override
              public List<T> get(int index) {
                  int start = index * chunkSize;
                  int end = Math.min(start + chunkSize, list.size());
      
                  if (start > end) {
                      throw new IndexOutOfBoundsException("Index " + index + " is out of the list range <0," + (size() - 1) + ">");
                  }
      
                  return new ArrayList<>(list.subList(start, end));
              }
      
              @Override
              public int size() {
                  return (int) Math.ceil((double) list.size() / (double) chunkSize);
              }
          }
      
      
      
      
      
      Function call : 
                    List<List<String>> containerNumChunks = PartitionUtil.ofSize(list, 999)
      

      更多详情:https://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/

      【讨论】:

      • 问题是关于 SQL,而不是 Java。这如何回答这个问题?
      • 在Java中,我们可以通过上述解决方案和任何编程语言来解决这个问题
      【解决方案4】:

      一些变通解决方案是:

      1。拆分IN子句

      将 IN 子句拆分为字面量小于 1000 的多个 IN 子句,并使用 OR 子句组合它们:

      将原来的“WHERE”子句从一个“IN”条件拆分为多个“IN”条件:

      Select id from x where id in (1, 2, ..., 1000,…,1500);
      

      收件人:

      Select id from x where id in (1, 2, ..., 999) OR id in (1000,...,1500);
      

      2。使用元组

      1000 的限制适用于单个项目的集合:(x) IN ((1), (2), (3), ...)。 如果集合包含两个或多个项目,则没有限制:(x, 0) IN ((1,0), (2,0), (3,0), ...):

      Select id from x where (x.id, 0) IN ((1, 0), (2, 0), (3, 0),.....(n, 0));
      

      3。使用临时表

      Select id from x where id in (select id from <temporary-table>);
      

      【讨论】:

      • 不错的总结。您知道各种选项之间是否存在性能差异吗?
      • 我的数据在 Java 列表中。我想知道使用 with 子句:with foo as (select :foo_1 id from dual union all ... select foo_n id from dual) select * from bar inner join foo on bar.id = foo.id 作为为每个查询创建临时表的替代方法。有cmets吗?
      • 2 很棒,它救了我很多次。 0 甚至比stackoverflow.com/a/17019130中的“魔术”更好
      【解决方案5】:

      还有一种解决方法可以分离您的数组,对我有用,因为使用某些旧框架很难实现其他解决方案。

      select * from tableA where id = 1 or id = 2 or id = 3 ...
      

      但为了获得更好的性能,如果可能的话,我会使用 Nikolai Nechai 的工会解决方案。

      【讨论】:

        【解决方案6】:

        还有另一种选择:with 语法。以 OP 为例,如下所示:

        with data as (
          select 'value1' name from dual
          union all
          select 'value2' name from dual
          union all
        ...
          select 'value10000+' name from dual)
        select field1, field2, field3 
        from table1 t1
        inner join data on t1.name = data.name;
        

        我遇到了这个问题。在我的例子中,我有一个 Java 数据列表,其中每个项目都有一个 item_id 和一个 customer_id。我在数据库中有两个表,分别订阅了各个客户的项目。我想获取所有订阅项目或该项目的客户的列表,以及项目 ID。

        我尝试了三种变体:

        1. Java 中的多项选择(使用元组绕过限制)
        2. With-语法
        3. 临时表

        选项 1:Java 中的多项选择

        基本上,我先

        select item_id, token 
        from item_subs 
        where (item_id, 0) in ((:item_id_0, 0)...(:item_id_n, 0))
        

        然后

        select cus_id, token 
        from cus_subs 
        where (cus_id, 0) in ((:cus_id_0, 0)...(:cus_id_n, 0))
        

        然后我在 Java 中构建一个 Map,其中 cus_id 作为键,项目列表作为值,对于每个找到的客户订阅,我添加(到从第一个选择返回的列表中)所有相关项目的条目item_id。代码比较混乱

        选项 2:With-syntax

        使用类似 SQL 的方式一次性获取所有内容

        with data as (
          select :item_id_0 item_id, :cus_id_0 cus_id
          union all
          ...
          select :item_id_n item_id, :cus_id_n cus_id )
        select I.item_id item_id, I.token token
        from item_subs I
        inner join data D on I.item_id = D.item_id
        union all
        select D.item_id item_id, C.token token
        from cus_subs C
        inner join data D on C.cus_id = D.cus_id
        

        选项 3:临时表

        创建一个包含三个字段的全局临时表:rownr(主键)、item_id 和 cus_id。在那里插入所有数据,然后运行与选项 2 非常相似的选择,但链接到临时表而不是 with data

        性能

        不是完全科学的性能分析。

        • 我正在针对一个开发数据库运行,我想要查找订阅的数据集中略多于 1000 行。
        • 我只尝试了一个数据集。
        • 我与我的数据库服务器不在同一个物理位置。它不是那么远,但我确实注意到,如果我在家里通过 VPN 尝试,那么它会慢得多,即使它是相同的距离(问题不是我的家庭互联网)。
        • 我正在测试完整的调用,所以我的 API 调用另一个(也在 dev 中的同一实例中运行),它也连接到数据库以获取初始数据集。但这三种情况都是一样的。

        YMMV。

        也就是说,临时表选项要慢得多。像双那么慢。选项 1 的时间为 14-15 秒,选项 2 的时间为 15-16 秒,选项 3 的时间为 30。

        我会从与数据库服务器相同的网络再次尝试它们,并在有机会时检查是否会改变事情。

        【讨论】:

          【解决方案7】:

          运营联盟

          select * from tableA where tableA.Field1 in (1,2,...999)
          union
          select * from tableA where tableA.Field1 in (1000,1001,...1999)
          union
          select * from tableA where tableA.Field1 in (2000,2001,...2999)
          

          【讨论】:

          • 这是最好的解决方案,因为它可以提高性能。只需使用“UNION ALL”而不是“UNION”即可获得最佳性能。
          【解决方案8】:

          我意识到这是一个老问题,并且指的是 TOAD,但如果您需要使用 c# 围绕这个进行编码,您可以通过 for 循环拆分列表。你基本上可以使用 subList() 对 Java 做同样的事情;

              List<Address> allAddresses = GetAllAddresses();
              List<Employee> employees = GetAllEmployees(); // count > 1000
          
              List<Address> addresses = new List<Address>();
          
              for (int i = 0; i < employees.Count; i += 1000)
              {
                  int count = ((employees.Count - i) < 1000) ? (employees.Count - i) - 1 : 1000;
                  var query = (from address in allAddresses
                               where employees.GetRange(i, count).Contains(address.EmployeeId)
                               && address.State == "UT"
                               select address).ToList();
          
                  addresses.AddRange(query);
              }
          

          希望这对某人有所帮助。

          【讨论】:

            【解决方案9】:

            请在in-clause 中使用内部查询:

            select col1, col2, col3... from table1
             where id in (select id from table2 where conditions...)
            

            【讨论】:

            • 可能使用内连接,在我们的例子中它显着加快了选择速度(8 秒 vs 50 毫秒)。
            • 假设您的 where 子句的数据在同一个数据库的另一个表中,并且您知道获取它的选择!并非总是如此
            【解决方案10】:

            另一种方式:

            CREATE OR REPLACE TYPE TYPE_TABLE_OF_VARCHAR2 AS TABLE OF VARCHAR(100);
            -- ...
            SELECT field1, field2, field3
              FROM table1
              WHERE name IN (
                SELECT * FROM table (SELECT CAST(? AS TYPE_TABLE_OF_VARCHAR2) FROM dual)
              );
            

            我不认为它是最佳的,但它确实有效。提示 /*+ CARDINALITY(...) */ 将非常有用,因为 Oracle 不了解传递的数组的基数并且无法估计最佳执行计划。

            作为另一种选择 - 批量插入临时表并使用子查询中的最后一个 IN 谓词。

            【讨论】:

              【解决方案11】:

              我最近遇到了这个问题,并想出了一个厚颜无耻的方法来解决这个问题,而无需将额外的 IN 子句串在一起

              你可以使用元组

              SELECT field1, field2, field3
              FROM table1
              WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
              

              Oracle 确实允许 >1000 个元组,但不允许简单值。更多关于这里,

              https://community.oracle.com/message/3515498#3515498

              https://community.oracle.com/thread/958612

              这当然是在您无法选择在 IN 中使用子查询从临时表中获取所需值的情况下。

              【讨论】:

                【解决方案12】:

                还有另一种方法可以解决此问题。假设您有两个表 Table1 和 Table2。并且需要使用 Criteria 查询获取 Table2 中未引用/不存在的所有 Table1 条目。所以就这样吧……

                List list=new ArrayList(); 
                Criteria cr=session.createCriteria(Table1.class);
                cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
                .
                .
                

                。 . .它将直接在 SQL 中执行所有子查询功能,而无需在由 Hibernate 框架转换的 SQL 中包含 1000 个或更多参数。它对我有用。注意:您可能需要根据需要更改 SQL 部分。

                【讨论】:

                  【解决方案13】:

                  只需使用多个子句来解决这个问题:

                  select field1, field2, field3 from table1 
                  where  name in ('value1', 'value2', ..., 'value999') 
                      or name in ('value1000', ..., 'value1999') 
                      or ...;
                  

                  【讨论】:

                  • 请记住,如果您想使用 NOT IN 逻辑,您需要将这些语句组合在一起
                  • 是的,这似乎是最好的答案,应该打勾。重要的是要注意列表中的限制是 1000 多个项目,将它们拆分到不同的列表中是一个聪明的解决方案,对我很有用。
                  • 这个解决方案对我来说效果很好,但它可能不是最具可扩展性的解决方案。
                  • 我正在使用一些辅助函数来生成 SQL 字符串。拆分成多个列表会使事情变得更加混乱(我想您需要括号以确保安全并将其分组为单个子句),但元组也是如此。有谁知道各种选项之间是否存在性能差异
                  • 在perl中怎么做?谁能帮我解决这个问题? stackoverflow.com/questions/62507305/…
                  猜你喜欢
                  • 2011-07-12
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-14
                  • 2020-09-28
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多