是否有ORA-01795的解决方法:列表中的最大表达式数是1000错误?

时间:2013-07-24 18:50:26

标签: sql toad

是否有

的解决方法

'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中执行查询,没有区别,同样的错误。如何修改查询以使其工作?

提前致谢

12 个答案:

答案 0 :(得分:100)

只需使用多个in-clauses来解决这个问题:

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

答案 1 :(得分:22)

我最近遇到了这个问题并找到了一种厚颜无耻的做法,而没有将其他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中使用子查询来从临时表中获取所需的值,那么这当然是可以的。

答案 2 :(得分:18)

一些解决方法是:

1-将IN子句拆分为多个IN子句,其中文字小于1000并使用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>);

答案 3 :(得分:7)

请使用in - 子句中的内部查询:

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

答案 4 :(得分:7)

还有一种方法:

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谓词。

答案 5 :(得分:3)

还有另一种解决此问题的方法。假设你有两个表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部分。

答案 6 :(得分:3)

还有另一种选择: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。我在DB中有两个表,用于订阅各个客户的项目。我想获得项目的所有订阅列表或该项目的客户以及项目ID。

我尝试了三种变体:

  1. 从Java中多次选择(使用元组来绕过极限)
  2. 使用的语法
  3. 临时表
  4. 选项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:使用语法

    使用像

    这样的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中的同一个实例中运行),它也连接到数据库以获取初始数据集。但在所有三种情况下都是一样的。

    因人而异。

    也就是说,临时表选项很多更慢。因为双倍如此缓慢。选项1的时间为14-15秒,选项2的时间为15-16,选项3的时间为30。

    我将从与DB服务器相同的网络再次尝试它们,并在我有机会时检查是否会改变这些内容。

答案 7 :(得分:2)

我意识到这是一个老问题而且指的是TOAD但是如果你需要使用c#编写代码,你可以通过for循环拆分列表。您可以使用subList();

基本上对Java执行相同的操作
public class ImageListAdapter extends ArrayAdapter {
private Context context;
private LayoutInflater inflater;

private String[] imageUrls;

public ImageListAdapter(Context context, String[] imageUrls) {
    super(context, R.layout.image_view, imageUrls);

    this.context = context;
    this.imageUrls = imageUrls;

    inflater = LayoutInflater.from(context);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (null == convertView) {
        convertView = inflater.inflate(R.layout.image_view, parent, false);
    }

    Picasso
            .with(context)
            .load(imageUrls[position])
            .fit() // will explain later
            .into((ImageView) convertView);

    return convertView;
}
}

希望这有助于某人。

答案 8 :(得分:2)

Operato union

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)

答案 9 :(得分:1)

    **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/

答案 10 :(得分:0)

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

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

但是为了获得更好的性能,如果可能的话,我将结合使用Nikolai Nechai的联合解决方案。

答案 11 :(得分:0)

传递列表和循环中大多数情况下需要返回的记录数=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;
}