2009-12-21 12 views
7

Parece que hay un límite de 1000 argumentos en Oracle SQL. Me encontré con esto cuando la generación de consultas como ....Límite de argumento SQL en Oracle

select * from orders where user_id IN(large list of ids over 1000) 

Mi solución es crear una tabla temporal, insertar los identificadores de usuario en el primer lugar de emitir una consulta a través de JDBC que tiene una lista gigante de los parámetros en el IN.

¿Alguien sabe de una solución más fácil? Dado que estamos usando Hibernate, me pregunto si automáticamente puede hacer una solución similar de forma transparente.

+0

¿Cómo se generó la gran lista de identificadores en primer lugar? ¿Vinieron de una tabla o consulta? (De alguna manera dudo que una IU haya generado tantos params ...) – YogoZuno

+0

esto ocurre a menudo cuando la lista de ID proviene de otra fuente, como un motor de búsqueda. es decir, es un método de correlación entre backends no integrados. – zzzeek

+0

Normalmente, los identificadores provienen del usuario que realiza operaciones masivas en la IU. – benstpierre

Respuesta

4

Un enfoque alternativo sería la de pasar una matriz a la base de datos y utilizar una función TABLE() en la cláusula IN. Esto probablemente funcionará mejor que una tabla temporal. Ciertamente será más eficiente que ejecutar múltiples consultas. Pero necesitará monitorear el uso de la memoria PGA si tiene una gran cantidad de sesiones haciendo esto. Además, no estoy seguro de lo fácil que será conectar esto a Hibernate.

Nota: Las funciones TABLE() funcionan en el motor SQL, por lo que necesitan que declaremos un tipo SQL.

create or replace type tags_nt as table of varchar2(10); 
/

La siguiente muestra rellena una matriz con un par de miles de etiquetas aleatorias. A continuación, utiliza la matriz en la cláusula IN de una consulta.

declare 
    search_tags tags_nt; 
    n pls_integer; 
begin 

    select name 
    bulk collect into search_tags 
    from (select name 
      from temp_tags 
      order by dbms_random.value) 
    where rownum <= 2000; 

    select count(*) 
    into n 
    from big_table 
    where name in (select * from table (search_tags)); 

    dbms_output.put_line('tags match '||n||' rows!'); 
end; 
/
3

Siempre que la tabla temporal sea una tabla temporal global (es decir, solo visible para la sesión), esta es la forma recomendada de hacer las cosas (y seguiría esa ruta por más de una docena de argumentos, y mucho menos mil).

Me pregunto dónde y cómo se está construyendo esa lista de 1000 argumentos. Si se trata de una agrupación semipermanente (por ejemplo, todos los empleados basados ​​en una ubicación particular), esa agrupación debe estar en la base de datos y la unión debe realizarse allí. Las bases de datos están diseñadas y construidas para hacer uniones realmente rápido. Mucho más rápido que llevar un montón de identificación a la mitad del nivel y luego enviarlos de vuelta a la base de datos.

select * from orders 
where user_id in 
(select user_id from users where location = :loc) 
1

los comentarios con respecto a "si estos identificadores están en su base de datos, utilice uniones/correlación en su lugar" mantener verdadero. Sin embargo, si su lista de ID proviene de otro lugar, como un resultado SOLR, puede sortear el requisito de tabla temporal emitiendo múltiples consultas, cada una con no más de 1000 identificadores presentes, y luego fusionando los resultados de la consulta en la memoria. Si coloca la lista inicial de identificadores en una colección única como un hashset, puede mostrar 1000 identificadores a la vez.

3

Puede añadir predicados adicionales para dividir la lista en trozos de 1000:

select * from orders where user_id IN (<first batch of 1000>) 
OR user_id IN (<second batch of 1000>) 
OR user_id IN ...