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
Antworten:
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 ...;
quelle
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>);
quelle
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
als Alternative zum Erstellen temporärer Tabellen für jede Abfrage. Irgendwelche Kommentare?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.
quelle
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
IN
Prädikat.quelle
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...)
quelle
Es gibt noch eine andere Option:
with
Syntax. 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:
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 von
with data
Performance
Dies ist keine vollständig wissenschaftliche Leistungsanalyse.
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.
quelle
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.
quelle
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.
quelle
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)
quelle
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.
quelle
**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/
quelle