2011-06-29 15 views
17

Estoy desarrollando una aplicación que procesa muchos datos en la base de datos Oracle.
En algún caso, tengo que obtener muchos objetos basados ​​en una lista dada de condiciones, y uso SELECT ...FROM.. WHERE... IN..., pero la expresión IN solo acepta una lista cuyo tamaño es máximo de 1,000 elementos.IN vs OR de Oracle, ¿qué más rápido?

Entonces uso la expresión OR, pero como observo, quizás esta consulta (usando OR) es más lenta que IN (con la misma lista de condiciones). ¿Es correcto? Y si es así, ¿cómo mejorar la velocidad de la consulta?

+0

¿La lista está estática o derivada de una consulta? – Phil

+0

No, la lista de valores a consultar se recuperó de un recurso externo. ¿Hay alguna manera de resolver este problema, porque mi lista es demasiado grande, puede contener más de 100000 elementos? –

+1

Por lo tanto, está creando una cadena de consulta masiva que contiene algo como IN (... 9997, 9998, 9999, 1000,1001. ..)? Eso en sí va a costar mucho, transmitir y analizar. No importa las posibilidades de inyección de sql. –

Respuesta

27

IN es preferible a OR - OR es un ejecutante notoriamente deficiente, y puede causar otros problemas que requerirían el uso de paréntesis en consultas complejas.

Mejor opción que IN o OR, es unir a una tabla que contiene los valores que desea (o no desea). Esta tabla para comparación se puede derivar, temporal o ya existente en su esquema.

+1

No, solo consulto en una sola tabla. Mi lista puede contener demasiados elementos, por lo tanto, no puedo usar IN. Traté de dividir la lista en partes más pequeñas y consultar en un lote de sublistas, pero más tarde tengo que pedir los datos en la memoria, es muy lento. –

+0

¿No están EN Y O SON lo mismo? Es decir, IN se expande a O de todos modos? Esta es la razón por la cual NOT IN con NULL falla – gbn

+0

@gbn: Lógicamente, sí. Pero 'IN' se optimiza frente a 'OR': es más que azúcar sintáctica. –

7

En este escenario Me gustaría hacer esto:

  1. Crear una columna de la tabla temporal global
  2. rellenar esta tabla con la lista de la fuente externa (y rápidamente - otra discusión general)
  3. Do su consulta uniendo la tabla temporal a la otra tabla (considere el muestreo dinámico ya que la tabla temporal no tendrá buenas estadísticas)

Esto significa que puede abandonar el género a la base de datos y escribe una consulta simple.

2

Me gustaría cuestionar el enfoque completo. El cliente del SP debe enviar 100000 ID. ¿De dónde saca el cliente esas identificaciones? Enviar una cantidad tan grande de ID como el parámetro del proceso va a costar significativamente de todos modos.

2

Oracle convierte internamente listas de EN a listas de OR de todos modos, por lo que realmente no debería haber diferencias de rendimiento. La única diferencia es que Oracle tiene que transformar los IN, pero tiene cadenas más largas para analizar si proporciona las RUP usted mismo.

Así es como se prueba eso.

CREATE TABLE my_test (id NUMBER); 

SELECT 1 
FROM my_test 
WHERE id IN (1,2,3,4,5,6,7,8,9,10, 
      21,22,23,24,25,26,27,28,29,30, 
      31,32,33,34,35,36,37,38,39,40, 
      41,42,43,44,45,46,47,48,49,50, 
      51,52,53,54,55,56,57,58,59,60, 
      61,62,63,64,65,66,67,68,69,70, 
      71,72,73,74,75,76,77,78,79,80, 
      81,82,83,84,85,86,87,88,89,90, 
      91,92,93,94,95,96,97,98,99,100 
      ); 

SELECT sql_text, hash_value 
FROM v$sql 
WHERE sql_text LIKE '%my_test%'; 

SELECT operation, options, filter_predicates 
FROM v$sql_plan 
WHERE hash_value = '1181594990'; -- hash_value from previous query 

instrucción SELECT
tabla de acceso completa ("ID" = 1 OR "ID" = 2 OR "ID" = 3 OR "ID" = 4 O "ID" = 5 OR "ID "= 6 O" ID "= 7 O" ID "= 8 O" ID "= 9 O" ID "= 10 O" ID "= 21 O " ID "= 22 O" ID "= 23 O" ID " = 24 O "ID" = 25 O "ID" = 26 O "ID" = 27 O "ID" = 28 O "ID" = 29 O "ID" = 30 O "ID" = 31 O "ID" = 32 O "ID" = 33 O "ID" = 34 O "ID" = 35 O "ID" = 36 O "ID" = 37 O "ID" = 38 O "ID" = 39 O "ID" = 40 O "ID" = 41 O "ID" = 42 O "ID" = 43 O "ID" = 44 O "ID" = 45 O "ID" = 46 O "ID" = 47 O "ID" = 48 O "ID" = 49 O "ID" = 50 O "ID" = 51 O R "ID" = 52 O "ID" = 53 O "ID" = 54 O "ID" = 55 O "ID" = 56 O "ID" = 57 O "ID" = 58 O "ID" = 59 O "ID" = 60 O "ID" = 61 O "ID" = 62 O "ID" = 63 O "ID" = 64 O "ID" = 65 O "ID" = 66 O "ID" = 67 O "ID" = 68 O "ID" = 69 O "ID" = 70 O "ID" = 71 O "ID" = 72 O "ID" = 73 O "ID" = 74 O "ID" = 75 O "ID" = 76 O "ID" = 77 O "ID" = 78 O "ID" = 79 O "ID" = 80 O "ID" = 81 O "ID" = 82 O "ID" = 83 O " ID "= 84 O" ID "= 85 O" ID "= 86 O" ID "= 87 O " ID "= 88 O" ID "= 89 O" ID "= 90 O" ID "= 91 O" ID "= 92 O" ID "= 93 O " ID "= 94 O" ID "= 95 O" ID "= 96 O" ID "= 97 O" ID "= 98 O" ID "= 99 O " ID "= 100)

+1

La tabla que ha creado es una tabla de pila, sin clave principal/índice –

+0

@OMPPonies Plus 1 en su respuesta para no rechazar JV como represalia. Sé que mucha gente aquí haría eso. – Mukus

1

Si crea la tabla con una clave principal:

CREATE TABLE my_test (id NUMBER, 
CONSTRAINT PK PRIMARY KEY (id)); 

y pasar por las mismas SELECTs para ejecutar la consulta con el múltiple en valores, seguido por recuperar el plan de ejecución a través valor hash, lo que se obtiene es:

SELECT STATEMENT 
INLIST ITERATOR 
INDEX     RANGE SCAN 

esto parece implicar que cuando se tiene una lista IN y está usando esto con una columna de PK, Oracle mantiene la lista internamente como "INLIST" porque es más eficiente para procesar esto, en lugar de convertir a ORs como en el caso de una tabla no indexada.

Estaba usando Oracle 10gR2 arriba.

Cuestiones relacionadas