Comment mettre plus de 1000 valeurs dans un Oracle dans la clause [dupliquer]

cette question a déjà une réponse ici:

  • SQL DANS la Clause 1000 point limite 4 réponses

y a-t-il un moyen de contourner la limite Oracle 10g de 1000 éléments dans une clause statique? J'ai une liste délimitée par des virgule de beaucoup d'IDs que je veux utiliser dans une clause IN, Parfois cette liste peut dépasser 1000 éléments, à quel point Oracle lance une erreur. La requête est semblable à cela...

select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
83
demandé sur Will 2008-12-30 16:35:17

11 réponses

mettez les valeurs dans une table temporaire et faites un select where id in (select id from temptable)

84
répondu Otávio Décio 2008-12-30 13:38:04

je suis presque sûr que vous pouvez diviser les valeurs entre plusieurs INs en utilisant ou:

select * from table1 where ID in (1,2,3,4,...,1000) or 
ID in (1001,1002,...,2000)
45
répondu Peter Severin 2008-12-31 11:16:48

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,...)
43
répondu rics 2009-08-15 09:21:02
select column_X, ... from my_table
where ('magic', column_X ) in (
        ('magic', 1),
        ('magic', 2),
        ('magic', 3),
        ('magic', 4),
             ...
        ('magic', 99999)
    ) ...
23
répondu Sergey11g 2017-05-15 14:00:03

Où trouve-t-on la liste des pièces d'identité? Puisque ce sont des identifiants dans votre base de données, proviennent-ils d'une requête antérieure?

quand j'ai vu cela dans le passé, c'est parce que: -

  1. une table de référence est manquante et la bonne façon serait d'ajouter la nouvelle table, de mettre un attribut sur cette table et de s'y joindre
  2. une liste d'id est extraite de la base de données, puis utilisé dans des déclaration SQL ultérieure (peut-être plus tard ou sur un autre serveur ou autre). Dans ce cas, la réponse est de ne jamais l'extraire de la base de données. Stocker dans une table temporaire ou tout simplement écrire une requête.

je pense qu'il pourrait y avoir de meilleures façons de retravailler ce code qui vient de faire fonctionner cette instruction SQL. Si vous donnez plus de détails, vous pourriez avoir quelques idées.

8
répondu WW. 2008-12-31 10:32:19

utiliser ...à partir de 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 la colonne id. L'un des avantages de cette approche est Qu'Oracle n'a pas besoin d'analyser la requête encore et encore. L'utilisation d'une table temporaire est la plupart du temps plus lente.

edition 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;
/

cette sortie:

Raymond
Hans
5
répondu tuinstoel 2009-08-15 09:17:10

j'ai fini ici à la recherche d'une solution.

selon le nombre haut de gamme d'articles que vous devez interroger, et en supposant que vos articles sont uniques, vous pouvez diviser votre requête en lots de requêtes de 1000 articles, et combiner les résultats sur votre fin à la place (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 être un bon compromis dans le scénario où vous n'avez généralement pas plus de 1000 points - plus de 1000 articles serait votre "haut de gamme" bord de scénario. Par exemple, dans le cas où vous avez 1500 articles, deux requêtes de (1000, 500) ne serait pas si mal. Cela suppose également que chaque requête n'est pas particulièrement coûteux en soi.

ce ne serait pas approprié si votre nombre typique d'éléments attendus est beaucoup plus grand - disons, dans la gamme de 100000 - nécessitant 100 requêtes. Si oui, alors vous devriez probablement regarder plus sérieusement en utilisant le global temporaire solution de tableaux fournie ci-dessus comme la solution la plus "correcte". De plus, si vos articles ne sont pas uniques, vous devrez résoudre les résultats en double dans vos lots ainsi.

3
répondu Mike Atlas 2012-06-14 20:20:07

Oui, situation très étrange pour oracle.

si vous spécifiez 2000 ids à l'intérieur de la clause IN, il échouera. cela échoue:

select ... 
where id in (1,2,....2000) 

mais si vous mettez simplement l'ids 2000 dans une autre table (table temp par exemple), il fonctionnera cela fonctionne:

select ... 
where id in (select userId 
             from temptable_with_2000_ids ) 

ce que vous pouvez faire, pourrait en fait diviser les enregistrements en un lot de 1000 enregistrements et les exécuter groupe par groupe.

1
répondu Aaron He 2013-05-08 20:00:05

au lieu d'utiliser IN clause, pouvez-vous essayer d'utiliser JOIN avec l'autre table, qui va chercher l'id. comme ça, on n'aura pas à s'inquiéter de limit. juste une pensée de mon côté.

0
répondu Raju 2012-08-01 14:35:29

voici un code Perl qui essaie de contourner la limite en créant une vue inline puis en sélectionnant à partir de celle-ci. Le texte de la déclaration est comprimé en utilisant des lignes de douze éléments chacun au lieu de sélectionner chaque élément du DUAL individuellement, puis non compressé en unionant ensemble toutes les colonnes. L'UNION ou L'UNION tout en décompression ne devrait faire aucune différence ici car tout va à l'intérieur d'un dans lequel imposera l'unicité avant de se joindre contre elle de toute façon, mais dans la compression, L'UNION tout est utilisé pour éviter beaucoup de comparaisons inutiles. Comme les données que je filtre sont toutes des nombres entiers, citer 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 l'utiliserait 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
0
répondu Never Sleep Again 2014-06-02 21:32:12

au lieu de SELECT * FROM table1 WHERE ID IN (1,2,3,4,...,1000);

utilisez ceci:

SELECT * FROM table1 WHERE ID IN (SELECT rownum AS ID FROM dual connect BY level <= 1000);

*notez que vous devez vous assurer que L'ID ne renvoie aucun autre ID étranger s'il s'agit d'une dépendance. Pour s'assurer que seuls les identificateurs existants sont disponibles, alors:

SELECT * FROM table1 WHERE ID IN (SELECT distinct(ID) FROM tablewhereidsareavailable);

Cheers

-2
répondu mousetwentytwo 2012-06-14 20:18:52