Có cách giải quyết nào cho ORA-01795: số biểu thức tối đa trong danh sách là 1000 lỗi không?


75

Có một giải pháp cho

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

Tôi có một truy vấn và nó đang chọn các trường dựa trên giá trị của một trường. Tôi đang sử dụng mệnh đề in và có hơn 10000 giá trị

thí dụ:

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

Mỗi khi thực hiện truy vấn, tôi nhận được ORA-01795: maximum number of expressions in a list is 1000 error. Tôi đang cố gắng thực hiện truy vấn trong TOAD, không có sự khác biệt, cùng một lỗi. Tôi sẽ sửa đổi truy vấn như thế nào để nó hoạt động?

Cảm ơn trước


2
đặt value1 .... value1000 + trong một bảng và chọn tên trong (chọn giá trị từ bảng)
basdwarf

2
Lỗi không phụ thuộc vào môi trường (ví dụ: SQL * Plus hoặc TOAD hoặc ...) nơi bạn thực hiện truy vấn của mình.
René Nyffenegger,

Câu trả lời:


120

Chỉ cần sử dụng nhiều mệnh đề trong để giải quyết vấn đề này:

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

5
Hãy ghi nhớ rằng nếu bạn đang muốn sử dụng NOT IN logic bạn cần VÀ các báo cáo lại với nhau
Keith Ritter

Có, đây có vẻ là câu trả lời tốt nhất và nên được đánh dấu. Điều quan trọng cần lưu ý là giới hạn của hơn 1000 mục trong danh sách, việc chia chúng thành nhiều danh sách khác nhau là một giải pháp thông minh và hữu ích đối với tôi.
theGabyRod

Giải pháp này hoạt động tốt đối với tôi, nhưng nó có lẽ không phải là giải pháp có thể mở rộng nhất.
juanheyns

Tôi đang sử dụng một số hàm trợ giúp để tạo chuỗi SQL. Việc chia thành nhiều danh sách làm cho điều này trở nên lộn xộn hơn (tôi đoán bạn cần dấu ngoặc ở phía an toàn và nhóm nó thành một mệnh đề duy nhất), nhưng bộ giá trị cũng vậy. Có ai biết nếu có một sự khác biệt hiệu suất giữa các tùy chọn khác nhau
Adam

Làm thế nào để làm điều đó trong perl? Bất cứ ai có thể giúp tôi với câu hỏi này? stackoverflow.com/questions/62507305/…
Biswajit Maharana

28

Một số giải pháp thay thế là:

1. Phân chia mệnh đề IN

Chia mệnh đề IN thành nhiều mệnh đề IN trong đó các ký tự nhỏ hơn 1000 và kết hợp chúng bằng cách sử dụng mệnh đề HOẶC:

Tách mệnh đề "WHERE" ban đầu từ một điều kiện "IN" thành một số điều kiện "IN":

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

Đến:

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

2. Sử dụng bộ giá trị

Giới hạn 1000 áp dụng cho các bộ sản phẩm đơn lẻ: (x) IN ((1), (2), (3), ...). Không có giới hạn nếu bộ chứa hai hoặc nhiều mục: (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. Sử dụng bảng tạm thời

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

2
Tóm tắt tốt đẹp. Bạn có biết nếu có sự khác biệt về hiệu suất giữa các tùy chọn khác nhau?
Adam

Tôi có dữ liệu của mình trong một danh sách bằng Java. Tôi đang băn khoăn về việc sử dụng mệnh đề có: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 thay thế cho việc tạo bảng tạm thời cho mỗi truy vấn. Có ý kiến ​​gì không?
Adam

24

Gần đây tôi đã gặp phải vấn đề này và đã tìm ra một cách giải quyết vấn đề mà không cần xâu chuỗi các mệnh đề IN bổ sung lại với nhau

Bạn có thể sử dụng Tuples

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

Oracle cho phép> 1000 Tuples nhưng không phải giá trị đơn giản. Thêm về điều này ở đây,

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

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

Điều này là tất nhiên nếu bạn không có tùy chọn sử dụng truy vấn con bên trong IN để lấy các giá trị bạn cần từ bảng tạm thời.


7

Một cách nữa:

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

Tôi không coi nó là tối ưu, nhưng nó hoạt động. Gợi ý /*+ CARDINALITY(...) */sẽ rất hữu ích vì Oracle không hiểu bản chất của mảng được truyền và không thể ước tính kế hoạch thực thi tối ưu.

Thay thế khác - chèn hàng loạt vào bảng tạm thời và sử dụng truy vấn con cuối cùng cho INvị từ.


7

Vui lòng sử dụng một truy vấn bên trong bên trong in-clause:

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

Có thể sử dụng tham gia bên trong, nó tăng tốc đáng kể lựa chọn trong trường hợp của chúng tôi (8 giây so với 50 mili giây).
jahav

1
Điều đó giả định dữ liệu của bạn cho mệnh đề where nằm trong một bảng khác trong cùng một DB và bạn biết lựa chọn để lấy nó! Không phải luôn luôn đúng
Adam

4

Có một tùy chọn khác: withcú pháp. Để sử dụng ví dụ OPs, điều này sẽ giống như sau:

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;

Tôi gặp phải vấn đề này. Trong trường hợp của tôi, tôi có một danh sách dữ liệu trong Java trong đó mỗi mục có item_id và customer_id. Tôi có hai bảng trong DB với các mục đăng ký cho các khách hàng tương ứng. Tôi muốn nhận danh sách tất cả các đăng ký cho các mặt hàng hoặc khách hàng cho mặt hàng đó, cùng với id mặt hàng.

Tôi đã thử ba biến thể:

  1. Nhiều lựa chọn từ Java (sử dụng các bộ giá trị để vượt qua giới hạn)
  2. Với cú pháp
  3. Bảng tạm thời

Tùy chọn 1: Nhiều lựa chọn từ Java

Về cơ bản, tôi đầu tiên

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

Sau đó

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

Sau đó, tôi tạo Bản đồ bằng Java với cus_id làm khóa và danh sách các mục dưới dạng giá trị, và đối với mỗi đăng ký khách hàng được tìm thấy, tôi thêm (vào danh sách được trả về từ lần chọn đầu tiên) một mục nhập cho tất cả các mục có liên quan với item_id đó. Đó là mã lộn xộn hơn nhiều

Tùy chọn 2: Với cú pháp

Nhận mọi thứ cùng một lúc với SQL như

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

Tùy chọn 3: Bảng tạm thời

Tạo một bảng tạm thời chung với ba trường: rownr (khóa chính), item_id và cus_id. Chèn tất cả dữ liệu vào đó rồi chạy một lựa chọn rất giống với tùy chọn 2, nhưng liên kết trong bảng tạm thời thay vìwith data

Hiệu suất

Đây không phải là một phân tích hiệu suất đầy đủ khoa học.

  • Tôi đang chạy với cơ sở dữ liệu phát triển, với hơn 1000 hàng trong tập dữ liệu mà tôi muốn tìm đăng ký.
  • Tôi chỉ thử một tập dữ liệu.
  • Tôi không ở cùng vị trí thực tế với máy chủ DB của mình. Nó không xa như vậy, nhưng tôi nhận thấy nếu tôi thử từ nhà qua VPN thì tất cả đều chậm hơn nhiều, mặc dù khoảng cách như nhau (và đó không phải là vấn đề với Internet tại nhà của tôi).
  • Tôi đang thử nghiệm cuộc gọi đầy đủ, vì vậy API của tôi gọi một lệnh khác (cũng chạy trong cùng một phiên bản trong dev) cũng kết nối với DB để nhận tập dữ liệu ban đầu. Nhưng điều đó đều giống nhau trong cả ba trường hợp.

YMMV.

Điều đó nói rằng, tùy chọn bảng tạm thời chậm hơn nhiều . Như trong gấp đôi quá chậm. Tôi nhận được 14-15 giây cho tùy chọn 1, 15-16 cho tùy chọn 2 và 30 cho tùy chọn 3.

Tôi sẽ thử lại chúng từ cùng một mạng với máy chủ DB và kiểm tra xem điều đó có thay đổi mọi thứ hay không khi tôi có cơ hội.


3

cũng có một cách khác để giải quyết vấn đề này. giả sử bạn có hai bảng Table1 và Table2. và nó được yêu cầu tìm nạp tất cả các mục nhập của Table1 không được tham chiếu / hiện diện trong Table2 bằng cách sử dụng truy vấn Tiêu chí. Vì vậy, hãy tiếp tục như thế này ...

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

. . . Nó sẽ thực hiện tất cả các chức năng truy vấn con trực tiếp trong SQL mà không bao gồm 1000 hoặc nhiều tham số trong SQL được chuyển đổi bởi Hibernate framework. Nó đã làm việc cho tôi. Lưu ý: Bạn có thể cần thay đổi phần SQL theo yêu cầu của bạn.


3

Tôi nhận ra đây là một câu hỏi cũ và đề cập đến TOAD nhưng nếu bạn cần viết mã xung quanh vấn đề này bằng cách sử dụng c #, bạn có thể chia danh sách thông qua vòng lặp for. Về cơ bản, bạn có thể làm tương tự với Java bằng cách sử dụng subList ();

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

Hy vọng điều này sẽ giúp ai đó.


3

Công đoàn Operato

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)

Đây là giải pháp tốt nhất vì nó tăng hiệu suất. Chỉ cần sử dụng "UNION ALL" thay vì "UNION" để đạt được hiệu suất tối đa.
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)

thêm chi tiết: https://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/


Câu hỏi là về SQL, không phải Java. Làm thế nào để trả lời câu hỏi này?
Noah Broyles

Trong Java, chúng ta có thể giải quyết vấn đề này bằng giải pháp trên và bất kỳ ngôn ngữ lập trình đó là một cách để giải pháp
Akhil Sabu

0

Ngoài ra còn có cách giải quyết khác để thực hiện việc chia tách mảng của bạn, phù hợp với tôi vì các giải pháp khác khó triển khai bằng cách sử dụng một số khuôn khổ cũ.

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

Nhưng để có hiệu suất tốt hơn, tôi sẽ sử dụng giải pháp của Nikolai Nechai với các công đoàn, nếu có thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.