Y at-il une solution de contournement pour
'ORA-01795: maximum number of expressions in a list is 1000 error'
J'ai une requête et il sélectionne des champs en fonction de la valeur d'un champ. J'utilise la clause in et il y a plus de 10000 valeurs
exemple:
select field1, field2, field3
from table1
where name in
(
'value1',
'value2',
...
'value10000+'
);
Chaque fois que j'exécute la requête, je reçois le ORA-01795: maximum number of expressions in a list is 1000 error
. J'essaie d'exécuter la requête dans TOAD, pas de différence, la même erreur. Comment pourrais-je modifier la requête pour la faire fonctionner?
Merci d'avance
Utilisez simplement plusieurs clauses dans pour contourner ce problème:
select field1, field2, field3 from table1
where name in ('value1', 'value2', ..., 'value999')
or name in ('value1000', ..., 'value1999')
or ...;
J'ai rencontré ce problème récemment et j'ai trouvé un moyen effronté de le faire sans regrouper les clauses IN supplémentaires.
Tu pourrais utiliser Tuples
SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
Oracle autorise> 1 000 Tuples, mais pas les valeurs simples. Plus à ce sujet ici,
https://community.Oracle.com/message/3515498#3515498
et
https://community.Oracle.com/thread/958612
C'est bien sûr si vous n'avez pas la possibilité d'utiliser une sous-requête dans IN pour obtenir les valeurs dont vous avez besoin à partir d'une table temporaire.
Certaines solutions de contournement sont:
1- Diviser une clause IN en plusieurs clauses IN dont le nombre est inférieur à 1000 et les combiner à l’aide de OR:
Fractionner la clause "WHERE" d'origine d'une condition "IN" en plusieurs conditions "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- Utilisation des multiplets: La limite de 1 000 s'applique aux ensembles d'éléments uniques: (x) IN ((1), (2), (3), ...). Il n'y a pas de limite si les ensembles contiennent deux ou plusieurs éléments: (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- Utilisation de la table temporaire:
Select id from x where id in (select id from <temporary-table>);
Une autre façon:
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)
);
Je ne considère pas que c'est optimal, mais ça marche. Le conseil /*+ CARDINALITY(...) */
serait très utile car Oracle ne comprend pas la cardinalité du tableau transmis et ne peut pas estimer le plan d'exécution optimal.
Autre alternative: insertion par lots dans la table temporaire et utilisation de la dernière sous-requête pour le prédicat IN
.
Veuillez utiliser une requête interne à l'intérieur de la clause in
-:
select col1, col2, col3... from table1
where id in (select id from table2 where conditions...)
il existe également un autre moyen de résoudre ce problème. Disons que vous avez deux tables Table1 et Table2. et il est nécessaire d'extraire toutes les entrées de Table1 non référencées/présentes dans la Table2 à l'aide de la requête de critères. Alors allez-y comme ça ...
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 )"));
.
.
. . . Il effectuera toute la fonction de sous-requête directement dans SQL sans inclure 1000 paramètres ou plus dans SQL convertis par le framework Hibernate. Cela a fonctionné pour moi. Remarque: Vous devrez peut-être modifier la partie SQL selon vos besoins.
Il existe une autre option: with
syntaxe. Pour utiliser l'exemple des PO, ceci ressemblerait à ceci:
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;
J'ai rencontré ce problème. Dans mon cas, j'avais une liste de données dans Java où chaque article avait un item_id et un customer_id. J'ai deux tables dans la base de données avec des abonnements aux articles des clients respectifs. Je veux obtenir une liste de tous les abonnements aux articles ou au client pour cet article, ainsi que l'identifiant de l'article.
J'ai essayé trois variantes:
Option 1: Choix multiples à partir de Java
Fondamentalement, j'ai d'abord
select item_id, token
from item_subs
where (item_id, 0) in ((:item_id_0, 0)...(:item_id_n, 0))
Ensuite
select cus_id, token
from cus_subs
where (cus_id, 0) in ((:cus_id_0, 0)...(:cus_id_n, 0))
Ensuite, je construis une carte dans Java avec le cus_id en tant que clé et une liste d'éléments en tant que valeur, et pour chaque abonnement client trouvé, j'ajoute (à la liste renvoyée par la première sélection) une entrée pour tous les éléments pertinents avec cet item_id. C'est un code beaucoup plus compliqué
Option 2: Avec syntaxe
Obtenez tout en une fois avec un SQL comme
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: Table temporaire
Créez une table temporaire globale avec trois champs: rownr (clé primaire), item_id et cus_id. Insérez toutes les données à cet endroit, puis exécutez une sélection très similaire à l'option 2, mais en liant la table temporaire à la place du with data
Performance
Ceci ( n'est pas une analyse de performance entièrement scientifique.
YMMV.
Cela dit, l'option de la table temporaire était beaucoup plus lente . Comme en double si lent. Je recevais 14-15 secondes pour l’option 1, 15-16 pour l’option 2 et 30 pour l’option 3.
Je vais les essayer à nouveau sur le même réseau que le serveur de base de données et vérifier si cela change les choses lorsque j'en ai l'occasion.
Syndicat des opérateurs
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)
Je me rends compte que c’est une vieille question qui fait référence à TOAD, mais si vous avez besoin de coder cela à l’aide de c #, vous pouvez scinder la liste en une boucle for. Vous pouvez essentiellement faire la même chose avec Java en utilisant 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);
}
J'espère que ça aide quelqu'un.
Il existe également une solution de contournement de votre tableau, qui a fonctionné pour moi, car d'autres solutions étaient difficiles à mettre en œuvre avec un ancien framework.
select * from tableA where id = 1 or id = 2 or id = 3 ...
Mais pour une meilleure performance, j'utiliserais la solution de Nikolai Nechai avec les syndicats, si possible.