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


75

有没有解决方法

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

提前致谢


2
将value1 .... value1000 +放在表中,然后选择名称(从表中选择值)
basdwarf

2
该错误不依赖于执行查询的环境(例如SQL * Plus或TOAD或...)。
勒内Nyffenegger

Answers:


120

只需使用多个条款即可解决此问题:

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

5
请记住,如果您想使用NOT IN逻辑,则需要将这些语句一起进行AND
Keith Ritter

是的,这似乎是最好的答案,应该打勾。重要的是要注意,限制是列表中的1000多个项目,将它们拆分成各种列表是一个聪明的解决方案,对我来说很有用。
theGabyRod '17

该解决方案对我来说效果很好,但它可能不是最具扩展性的解决方案。
juanheyns

我正在使用一些辅助函数来生成SQL字符串。拆分成多个列表会使此消息变得更加混乱(我想您需要放在方括号中才能确保安全,并将其分组为单个子句),但元组也是如此。有人知道各种选项之间是否存在性能差异
亚当

在perl中如何做?有人可以帮我解决这个问题吗?stackoverflow.com/questions/62507305/...
Biswajit Maharana

28

一些解决方法是:

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,3,(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>);

2
不错的总结。您是否知道各种选项之间的性能差异?
亚当

我的数据放在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作为为每个查询创建临时表的替代方法。任何意见?
亚当

24

我最近遇到了这个问题,并发现了一种轻松的方法,而无需将其他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内使用子查询来从临时表中获取所需值的方法,那当然是这样。


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无法理解传递的数组的基数,并且无法估计最佳执行计划。

另一种选择是-将批处理批量插入临时表,并使用子查询中的last作为IN谓词。


7

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

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

可能使用内部联接,在我们的情况下,它显着加快了选择速度(8秒vs 50 ms)。
jahav

1
假定您的where子句数据在同一数据库的另一个表中,并且您知道要获取它的选择!并非总是如此
亚当

4

还有另一种选择: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. 带语法
  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))

然后,我以cus_id为键,并以商品列表作为值,用Java构建了一个Map,然后为每个找到的客户订阅添加(到第一个选择返回的列表中)具有该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的同一实例中运行),该API也连接到数据库以获取初始数据集。但这三种情况都是一样的。

YMMV。

也就是说,临时表选项慢得多。如双倍慢。选项1获得14-15秒,选项2获得15-16,选项3获得30。

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


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部分。


3

我意识到这是一个古老的问题,并涉及到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);
    }

希望这对某人有帮助。


3

歌剧工会

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”即可获得最佳性能。
DanielCuadra

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/


问题是关于SQL,而不是Java。这如何回答这个问题?
诺亚·布鲁克斯

在Java中,我们可以通过上述解决方案以及任何一种解决方案的编程语言来解决此问题
Akhil Sabu

0

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.