Est-il possible de contourner la limitation Oracle 10g de 1 000 éléments dans une clause statique IN? J'ai une liste délimitée par des virgules de nombreux ID que je souhaite utiliser dans une clause IN. Parfois, cette liste peut contenir plus de 1 000 éléments. Oracle renvoie alors une erreur. La requête est similaire à celle-ci ...
select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
Placez les valeurs dans une table temporaire puis effectuez un select où id dans (select id de tentant)
Je suis presque sûr que vous pouvez fractionner les valeurs sur plusieurs RI en utilisant OU:
select * from table1 where ID in (1,2,3,4,...,1000) or
ID in (1001,1002,...,2000)
Vous pouvez essayer d'utiliser le formulaire suivant:
select * from table1 where ID in (1,2,3,4,...,1000)
union all
select * from table1 where ID in (1001,1002,...)
select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
) ...
Où obtenez-vous la liste des identifiants en premier lieu? S'ils sont des identifiants dans votre base de données, sont-ils issus d'une requête précédente?
Quand j'ai vu cela dans le passé, c'était parce que: -
Je pense qu'il y a peut-être de meilleures façons de retravailler ce code en appliquant simplement cette instruction SQL. Si vous fournissez plus de détails, vous aurez peut-être des idées.
Utilisez ... depuis la table (...:
create or replace type numbertype
as object
(nr number(20,10) )
/
create or replace type number_table
as table of numbertype
/
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select *
from employees , (select /*+ cardinality(tab 10) */ tab.nr from table(p_numbers) tab) tbnrs
where id = tbnrs.nr;
end;
/
C'est l'un des rares cas où vous avez besoin d'un indice, sinon Oracle n'utilisera pas l'index sur l'id de la colonne. L'un des avantages de cette approche est qu'Oracle n'a pas besoin d'analyser la requête à plusieurs reprises. L'utilisation d'une table temporaire est la plupart du temps plus lente.
edit 1 simplifié la procédure (merci à jimmyorr) + exemple
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select /*+ cardinality(tab 10) */ emp.*
from employees emp
, table(p_numbers) tab
where tab.nr = id;
end;
/
Exemple:
set serveroutput on
create table employees ( id number(10),name varchar2(100));
insert into employees values (3,'Raymond');
insert into employees values (4,'Hans');
commit;
declare
l_number number_table := number_table();
l_sys_refcursor sys_refcursor;
l_employee employees%rowtype;
begin
l_number.extend;
l_number(1) := numbertype(3);
l_number.extend;
l_number(2) := numbertype(4);
tableselect(l_number, l_sys_refcursor);
loop
fetch l_sys_refcursor into l_employee;
exit when l_sys_refcursor%notfound;
dbms_output.put_line(l_employee.name);
end loop;
close l_sys_refcursor;
end;
/
Cela produira:
Raymond
Hans
Je me suis retrouvé ici à la recherche d'une solution.
En fonction du nombre d'éléments haut de gamme sur lesquels vous devez effectuer une requête et en supposant que vos éléments soient uniques, vous pouvez fractionner votre requête en lots de requêtes de 1 000 éléments et combiner les résultats de votre côté (pseudocode ici):
//remove dupes
items = items.RemoveDuplicates();
//how to break the items into 1000 item batches
batches = new batch list;
batch = new batch;
for (int i = 0; i < items.Count; i++)
{
if (batch.Count == 1000)
{
batches.Add(batch);
batch.Clear()
}
batch.Add(items[i]);
if (i == items.Count - 1)
{
//add the final batch (it has < 1000 items).
batches.Add(batch);
}
}
// now go query the db for each batch
results = new results;
foreach(batch in batches)
{
results.Add(query(batch));
}
Cela peut constituer un bon compromis dans le cas où vous ne disposez généralement pas de plus de 1 000 éléments, car avoir plus de 1 000 éléments constituerait votre scénario "haut de gamme". Par exemple, si vous avez 1 500 éléments, deux requêtes de (1000, 500) ne seraient pas si mauvaises. Cela suppose également que chaque requête n'est pas particulièrement onéreuse.
Ceci ne serait pas serait approprié si votre nombre typique d'éléments attendus devait être beaucoup plus important - disons de l'ordre de 100 000 - nécessitant 100 requêtes. Si tel est le cas, vous devriez probablement vous pencher plus sérieusement sur l’utilisation de la solution globale de tables temporaires fournie ci-dessus en tant que solution la plus "correcte". De plus, si vos articles ne sont pas uniques, vous devrez également résoudre les doublons dans vos lots.
Oui, situation très étrange pour Oracle.
si vous spécifiez 2000 ID dans la clause IN, la tentative échouera. cela échoue:
select ...
where id in (1,2,....2000)
mais si vous mettez simplement les 2000 ids dans une autre table (table temp par exemple), cela fonctionnera comme suit:
select ...
where id in (select userId
from temptable_with_2000_ids )
ce que vous pouvez faire peut en fait diviser les enregistrements en un grand nombre d’enregistrements et les exécuter groupe par groupe.
Au lieu d'utiliser la clause IN
, pouvez-vous essayer d'utiliser JOIN
avec l'autre table, qui récupère l'identifiant. De cette façon, nous n'avons pas à nous soucier de la limite. juste une pensée de mon côté.
Voici un code Perl qui tente de contourner la limite en créant une vue intégrée, puis en effectuant une sélection. Le texte de l'instruction est compressé en utilisant des lignes de douze éléments au lieu de sélectionner chaque élément individuellement dans DUAL, puis non compressé en unissant toutes les colonnes. UNION ou UNION ALL en décompression ne devrait pas faire de différence ici, car tout se passe dans un IN qui imposera l’unicité avant de le rejoindre, mais dans la compression, UNION ALL est utilisé pour éviter de nombreuses comparaisons inutiles. Comme les données que je filtre sont des nombres entiers, la citation n’est pas un problème.
#
# generate the innards of an IN expression with more than a thousand items
#
use English '-no_match_vars';
sub big_IN_list{
@_ < 13 and return join ', ',@_;
my $padding_required = (12 - (@_ % 12)) % 12;
# get first dozen and make length of @_ an even multiple of 12
my ($a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l) = splice @_,0,12, ( ('NULL') x $padding_required );
my @dozens;
local $LIST_SEPARATOR = ', '; # how to join elements within each dozen
while(@_){
Push @dozens, "SELECT @{[ splice @_,0,12 ]} FROM DUAL"
};
$LIST_SEPARATOR = "\n union all\n "; # how to join @dozens
return <<"EXP";
WITH t AS (
select $a A, $b B, $c C, $d D, $e E, $f F, $g G, $h H, $i I, $j J, $k K, $l L FROM DUAL
union all
@dozens
)
select A from t union select B from t union select C from t union
select D from t union select E from t union select F from t union
select G from t union select H from t union select I from t union
select J from t union select K from t union select L from t
EXP
}
On utiliserait ça comme ça:
my $bases_list_expr = big_IN_list(list_your_bases());
$dbh->do(<<"UPDATE");
update bases_table set belong_to = 'us'
where whose_base in ($bases_list_expr)
UPDATE