Gibt es eine Problemumgehung für ORA-01795: Die maximale Anzahl von Ausdrücken in einer Liste beträgt 1000 Fehler?

75

Gibt es eine Problemumgehung für

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

Ich habe eine Abfrage und sie wählt Felder basierend auf dem Wert eines Feldes aus. Ich verwende die in-Klausel und es gibt mehr als 10000 Werte

Beispiel:

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

Jedes Mal, wenn ich die Abfrage ausführe, erhalte ich die ORA-01795: maximum number of expressions in a list is 1000 error. Ich versuche die Abfrage in TOAD auszuführen, kein Unterschied, der gleiche Fehler. Wie würde ich die Abfrage ändern, damit sie funktioniert?

Danke im Voraus

valmont74
quelle
2
setze value1 .... value1000 + in eine Tabelle und wähle den Namen in (wähle den Wert aus der Tabelle aus)
basdwarf
2
Der Fehler hängt nicht von der Umgebung ab (z. B. SQL * Plus oder TOAD oder ...), in der Sie Ihre Abfrage ausführen.
René Nyffenegger

Antworten:

120

Verwenden Sie einfach mehrere In-Klauseln, um dies zu umgehen:

select field1, field2, field3 from table1 
where  name in ('value1', 'value2', ..., 'value999') 
    or name in ('value1000', ..., 'value1999') 
    or ...;
Fabian Barney
quelle
5
Denken Sie daran, dass Sie, wenn Sie NOT IN-Logik verwenden möchten, diese Anweisungen UND-Anweisungen zusammen verwenden müssen
Keith Ritter
Ja, dies scheint die beste Antwort zu sein und sollte angekreuzt werden. Es ist wichtig zu beachten, dass die Beschränkung auf mehr als 1000 Elemente in der Liste besteht. Das Aufteilen in verschiedene Listen ist eine clevere Lösung und für mich nützlich.
TheGabyRod
Diese Lösung hat bei mir gut funktioniert, ist aber wahrscheinlich nicht die skalierbarste Lösung.
Juanheyns
Ich verwende einige Hilfsfunktionen, um die SQL-Zeichenfolgen zu generieren. Das Aufteilen in mehrere Listen macht dies unübersichtlicher (ich denke, Sie benötigen Klammern, um auf der sicheren Seite zu sein und sie als einzelne Klausel zu gruppieren), aber auch Tupel. Weiß jemand, ob es einen Leistungsunterschied zwischen den verschiedenen Optionen gibt
Adam
Wie mache ich das in Perl? Kann mir jemand bei dieser Frage helfen? stackoverflow.com/questions/62507305/…
Biswajit Maharana
27

Einige Problemumgehungslösungen sind:

1. Teilen Sie die IN-Klausel auf

Teilen Sie die IN-Klausel in mehrere IN-Klauseln auf, bei denen Literale kleiner als 1000 sind, und kombinieren Sie sie mit OR-Klauseln:

Teilen Sie die ursprüngliche "WHERE" -Klausel von einer "IN" -Bedingung in mehrere "IN" -Bedingungen auf:

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

Zu:

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

2. Verwenden Sie Tupel

Das Limit von 1000 gilt für Sätze einzelner Elemente: (x) IN ((1), (2), (3), ...). Es gibt keine Begrenzung, wenn die Sätze zwei oder mehr Elemente enthalten: (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. Verwenden Sie eine temporäre Tabelle

Select id from x where id in (select id from <temporary-table>);
Ahmed MANSOUR
quelle
2
Schöne Zusammenfassung. Wissen Sie, ob es einen Leistungsunterschied zwischen den verschiedenen Optionen gibt?
Adam
Ich habe meine Daten in einer Liste in Java. Ich frage mich, ob ich eine with-Klausel verwenden soll: 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.idals Alternative zum Erstellen temporärer Tabellen für jede Abfrage. Irgendwelche Kommentare?
Adam
24

Ich bin kürzlich auf dieses Problem gestoßen und habe eine freche Methode gefunden, ohne zusätzliche IN-Klauseln aneinander zu reihen

Sie könnten Tupel verwenden

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

Oracle erlaubt> 1000 Tupel, aber keine einfachen Werte. Mehr dazu hier,

https://community.oracle.com/message/3515498#3515498
und
https://community.oracle.com/thread/958612

Dies ist natürlich der Fall, wenn Sie nicht die Möglichkeit haben, eine Unterabfrage in IN zu verwenden, um die benötigten Werte aus einer temporären Tabelle abzurufen.

MangoCrysis
quelle
7

Noch ein Weg:

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

Ich halte es nicht für optimal, aber es funktioniert. Der Hinweis /*+ CARDINALITY(...) */wäre sehr nützlich, da Oracle die Kardinalität des übergebenen Arrays nicht versteht und den optimalen Ausführungsplan nicht schätzen kann.

Als weitere Alternative - Batch-Einfügung in temporäre Tabelle und Verwendung der letzten Unterabfrage als INPrädikat.

svaor
quelle
7

Bitte verwenden Sie eine innere Abfrage innerhalb der in-Klausel:

select col1, col2, col3... from table1
 where id in (select id from table2 where conditions...)
Vikas Kumar
quelle
Mögliche Verwendung der inneren Verknüpfung, hat in unserem Fall die Auswahl erheblich beschleunigt (8 Sekunden gegenüber 50 ms).
Jahav
1
Dies setzt voraus, dass sich Ihre Daten für die where-Klausel in einer anderen Tabelle in derselben Datenbank befinden und Sie die Auswahl kennen, um sie abzurufen! Nicht immer wahr
Adam
4

Es gibt noch eine andere Option: withSyntax. Um das OP-Beispiel zu verwenden, würde dies folgendermaßen aussehen:

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;

Ich bin auf dieses Problem gestoßen. In meinem Fall hatte ich eine Liste von Daten in Java, wobei jeder Artikel eine item_id und eine customer_id hatte. Ich habe zwei Tabellen in der DB mit Abonnements für Artikel der jeweiligen Kunden. Ich möchte eine Liste aller Abonnements für die Artikel oder den Kunden für diesen Artikel zusammen mit der Artikel-ID erhalten.

Ich habe drei Varianten ausprobiert:

  1. Mehrfachauswahl aus Java (mit Tupeln, um das Limit zu umgehen)
  2. With-Syntax
  3. Temporärer Tisch

Option 1: Mehrfachauswahl aus Java

Grundsätzlich habe ich zuerst

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

Dann

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

Dann erstelle ich eine Karte in Java mit der cus_id als Schlüssel und einer Liste von Elementen als Wert und füge für jedes gefundene Kundenabonnement (zu der Liste, die von der ersten Auswahl zurückgegeben wurde) einen Eintrag für alle relevanten Elemente mit dieser item_id hinzu. Es ist viel chaotischer Code

Option 2: With-Syntax

Holen Sie sich alles auf einmal mit einem SQL wie

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

Option 3: Temporäre Tabelle

Erstellen Sie eine globale temporäre Tabelle mit drei Feldern: rownr (Primärschlüssel), item_id und cus_id. Fügen Sie dort alle Daten ein und führen Sie dann eine sehr ähnliche Auswahl wie Option 2 aus, verknüpfen Sie sie jedoch in der temporären Tabelle anstelle vonwith data

Performance

Dies ist keine vollständig wissenschaftliche Leistungsanalyse.

  • Ich verwende eine Entwicklungsdatenbank mit etwas mehr als 1000 Zeilen in meinem Datensatz, für die ich Abonnements finden möchte.
  • Ich habe nur einen Datensatz ausprobiert.
  • Ich bin nicht am selben physischen Standort wie mein DB-Server. Es ist nicht so weit weg, aber ich merke, wenn ich es von zu Hause aus über das VPN versuche, ist alles viel langsamer, obwohl es die gleiche Entfernung ist (und es ist nicht mein Heim-Internet, das das Problem ist).
  • Ich habe den vollständigen Aufruf getestet, daher ruft meine API einen anderen auf (der ebenfalls in derselben Instanz in dev ausgeführt wird), der ebenfalls eine Verbindung zur Datenbank herstellt, um den ursprünglichen Datensatz abzurufen. Das ist aber in allen drei Fällen gleich.

YMMV.

Die Option für temporäre Tabellen war jedoch viel langsamer. Wie im Doppel so langsam. Ich bekam 14-15 Sekunden für Option 1, 15-16 für Option 2 und 30 für Option 3.

Ich werde sie erneut im selben Netzwerk wie der DB-Server versuchen und prüfen, ob sich dadurch etwas ändert, wenn ich die Gelegenheit dazu bekomme.

Adam
quelle
3

Es gibt auch eine andere Möglichkeit, dieses Problem zu beheben. Angenommen, Sie haben zwei Tabellen, Tabelle1 und Tabelle2. und es ist erforderlich, alle Einträge von Tabelle 1, auf die in Tabelle 2 nicht verwiesen wird / vorhanden ist, unter Verwendung der Kriterienabfrage abzurufen. Also mach so weiter ...

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

. . . Alle Unterabfragefunktionen werden direkt in SQL ausgeführt, ohne dass 1000 oder mehr Parameter in SQL enthalten sind, die vom Hibernate-Framework konvertiert wurden. Es hat bei mir funktioniert. Hinweis: Möglicherweise müssen Sie den SQL-Teil gemäß Ihren Anforderungen ändern.

Fahim Ashraf
quelle
3

Mir ist klar, dass dies eine alte Frage ist und sich auf TOAD bezieht, aber wenn Sie dies mit c # codieren müssen, können Sie die Liste durch eine for-Schleife aufteilen. Mit Java können Sie im Wesentlichen dasselbe mit subList () tun.

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

Hoffe das hilft jemandem.

Tyler
quelle
3

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)
Nikolai Nechai
quelle
Dies ist die beste Lösung, da sie die Leistung steigert. Verwenden Sie einfach "UNION ALL" anstelle von "UNION", um maximale Leistung zu erzielen.
DanielCuadra
0

Es gibt auch eine Problemumgehung für die Disjunktion Ihres Arrays, die für mich funktioniert hat, da andere Lösungen mit einem alten Framework nur schwer zu implementieren waren.

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

Aber für eine bessere Leistung würde ich, wenn möglich, die Lösung von Nikolai Nechai mit Gewerkschaften verwenden.

El El
quelle
0
    **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)

Weitere Details: https://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/

Akhil Sabu
quelle
Die Frage bezieht sich auf SQL, nicht auf Java. Wie beantwortet dies die Frage?
Noah Broyles
In Java können wir dieses Problem mit der oben genannten Lösung lösen, und jede Programmiersprache ist eine Lösung
Akhil Sabu