2009-07-07 7 views
5

Leí parte del debate en this question y pensé que en mi código PL/SQL tengo consultas de estilo "exists" por todas partes que no usan la optimización ROWNUM = 1.Bajo qué condiciones ROWNUM = 1 aumenta significativamente el rendimiento en una consulta syle "exists"

Las preguntas que tengo son:

  1. ¿La introducción de ROWNUM = 1 aumentar significativamente el rendimiento?
  2. Si es así, bajo qué condiciones el rendimiento mejoras, en particular (por ejemplo, un montón de combinaciones, restricciones en las columnas no indexadas, mesas grandes, grandes conjuntos de resultados)

que estoy tratando de determinar de que vale la pena volver a escribir todo de mis consultas existentes para agregar una optimización ROWNUM = 1.

Las consultas en las que estoy pensando son las que pueden tener varias combinaciones y pueden consultar tablas grandes. Tienen la forma general de:

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions>; 

IF ln_count > 0 THEN 
    <do stuff> 
END IF; 

estoy considerando cambiar a:

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions> 
AND ROWNUM = 1; 

IF <local variable> > 0 THEN 
    <do stuff> 
END IF; 
+0

¿Su primera consulta pretende tener SELECT COUNT (1) en lugar de SELECT 1? –

+0

SELECCIONE 1 (es decir, seleccione un valor constante para las filas devueltas en lugar de contar el número de filas) – darreljnz

Respuesta

7

Mejora significativamente el rendimiento (decenas de porcentaje promedio) en las consultas que no se pueden resolver mediante la simple búsqueda de índice individual, p. la mesa se une. Sin embargo, tiene el potencial de ocultar datos/error de aplicación.

Deja para tener una tabla:

create table t (id number(10,0), padding varchar2(1000)); 

--intentionally no utilice PK a que el ejemplo sea lo más simple posible. El relleno se utiliza para simular la carga de datos reales en cada registro

con muchos registros:

insert into t (id, padding) 
select rownum, rpad(' ', 1000) from dual connect by level < 10000 

Ahora bien, si le preguntas algo así como

select 1 into ll_exists 
from t where id = 5; 

el PP tiene que pasar por toda la tabla si encontró el único registro coincidente en el primer bloque de datos (que, por cierto, no podemos saber porque podría insertarse de muchas formas diferentes) o en el último. Eso es porque no sabe que solo hay un registro coincidente. Por otro lado, si usa ... y rownum = 1, puede detener el recorrido a través de los datos después de encontrar el registro porque le indicó que no hay (o no es necesario) otro registro coincidente.

El inconveniente es que con la restricción Rownum puede obtener resultados no deterministas si los datos contienen más de un registro posible. Si la consulta fue

select id into ll_id 
from t where mod (id, 2) = 1 
and rownum = 1; 

entonces puedo recibo de la respuesta DB 1, así como 3, así como 123 ... Para no está garantizada y esta es la consecuencia. (sin la cláusula rownum obtendría una excepción TOO_MANY_ROWS. Depende de la situación que sea peor)

Si realmente desea una consulta que pruebe la existencia, ESCRÍBELO DE ESA FORMA.

begin 

select 'It does' 
    into ls_exists 
from dual where 
exists (your_original_query_without_rownum); 

do_something_when_it_does_exist 
exception 
    when no_data_found then 
    do_something_when_it_doesn't_exist 
end; 
+0

+1, buena respuesta detallada + notas sobre la naturaleza no determinista de los predicados rownum. –

+0

Me gusta la conclusión al final, esa es una forma más apropiada de escribirlo. – darreljnz

+0

La consulta externa puede seleccionar conteo (*) desde dual ...(será wither 1 o 0), por lo que no necesitará usar el bloque de excepción para hacer la lógica del programa (que es malo). –

2

Voy a suponer que esto no va a ser digno de su tiempo. Los optimizadores modernos son muy buenos en lo que hacen, así que me parece un poco sorprendente que una consulta que solo tiene PERMITIDO devolver una fila vea un aumento significativo en el rendimiento al agregar ROWNUM = 1.

¿Supuestamente la ganancia de rendimiento elimina la necesidad de verificar esta restricción?

encuentro cuando dejo de confiar en que el optimizador A menudo cavar una fosa más profunda a mí mismo;)

Además: En caso de duda probarlo. Encuentre una unión grande, ejecútela varias veces sin rownum = 1, varias veces con rownum = 1 y vea si nota un gran porcentaje de mejora. Para garantizar que no haya problemas de almacenamiento en caché, le sugiero que haga esto en una base de datos que pueda reiniciar.

+1

Todos pasamos nuestros primeros 2 o 3 años aprendiendo SQL, no hay nuevas ideas inteligentes. – dkretz

+0

Las consultas están "permitidas" para devolver una fila, pero se ignora cualquier cosa después de la primera, ya que solo me importa si hay algún resultado. Un ejemplo práctico de mi código. Tengo una gran tabla que contiene pedidos de clientes. Cada pedido puede tener un estado (por ejemplo, "Pendiente", "Abierto", "Cerrado", "Cancelado") y un identificador de producto. Tengo una consulta para averiguar si hay pedidos abiertos para un identificador de producto determinado. Sé que, en teoría, hay un aumento en el rendimiento; lo que me interesa es si hay un aumento significativo en el rendimiento en la práctica. – darreljnz

+0

No estoy de acuerdo. Existe una diferencia entre una consulta que "permite devolver una fila" (por ejemplo, una que calcula un agregado) y una que solo examina una fila (por ejemplo, una con ROWNUM = 1). Comparar: SELECCIONAR CUENTA (*) DE my_big_table; frente a SELECT COUNT (*) FROM my_big_table WHERE ROWNUM = 1; - el resultado (en términos de rendimiento) será muy diferente. –

5

Una regla de oro en la optimización es no hacerlo a menos que tenga un punto de acceso que deba corregir. Sin embargo, si tiene curiosidad acerca de los beneficios de rendimiento, es posible que desee ejecutar algunas pruebas con ambos para ver si puede medir cualquier rendimiento mejorado.

wikipedia quotes Donald Knuth como diciendo:

"Debemos olvidarnos de pequeñas eficiencias, dicen que alrededor del 97% del tiempo: la optimización prematura es la raíz de todo mal".

+0

+1, buen consejo y buena cita – DCookie

0

Si solicita COUNT (1), entonces Oracle debe encontrar todas las filas correspondientes para satisfacer su respuesta exacta.

SELECT COUNT(1) FROM .... 

Si solicita 1 de la primera fila, Oracle puede detenerse una vez que haya encontrado una fila coincidente.

SELECT 1 FROM ... WHERE ROWNUM = 1 

Es una buena práctica sólo se piden los datos que realmente se necesita. ¿Por qué Oracle le dice que hay 1,203,499 resultados coincidentes cuando solo le importa el primero?La gente ha mencionado que el optimizador puede mejorar las cosas. Sin embargo, todavía debe responder la pregunta que hizo. Si hace una pregunta más fácil, puede obtener la respuesta más rápido.

Las veces es probable que tenga un impacto significativo en el rendimiento: - * El recuento real de registros que se encuentra es alta, * Oracle cambia de una combinación hash plan para un bucle anidado, y el plan de bucle anidado se mejor para encontrar la primera fila

+0

COUNT (1) no significa que solo obtendrá una fila. SELECCIONAR COUNT (*) FROM my_big_table; es exactamente lo mismo que SELECT COUNT (1) FROM my_big_table ;. –

+0

No estaba haciendo un punto sobre COUNT (*) versus COUNT (1). Si solicita COUNT (1), entonces debe contarlos todos. Si solicita 1 para la primera fila coincidente, solo necesita encontrar una. –

+0

Bastante justo. Personalmente evito usar "COUNT (1)" para evitar confusiones para los novatos. –

0

¿La introducción de ROWNUM = 1 aumenta significativamente el rendimiento?

Puede marcar una gran diferencia. Si solo está interesado en la primera fila que encuentra la base de datos cuando ejecuta la consulta, lo mejor es decirle a Oracle que, agregando "ROWNUM = 1". Si no lo hace, Oracle asumirá que tiene la intención de recuperar todas las filas de la consulta, y optimizará la consulta en consecuencia.

En el caso de COUNT(), si solo quiere saber si hay al menos un registro, el optimizador de consultas no lo sabrá y contará todas y cada una de las filas, una pérdida de tiempo. Si agrega ROWNUM = 1, le ha dado al optimizador la oportunidad de detenerse tan pronto como encuentre una fila.

Si es así, bajo qué condiciones el rendimiento mejoras, en particular (por ejemplo, una gran cantidad de combinaciones, restricciones en las columnas no indexadas, mesas grandes, grandes conjuntos de resultados)

Cuantos más datos de la consulta necesita Arar para responder a su consulta, más probable es que el predicado ROWNUM = 1 mejore el rendimiento. En una combinación de múltiples tablas, por ejemplo, agregar ROWNUM = 1 podría cambiar el plan de usar muchas combinaciones de hash costosas, para hacer que use loops anidados, que son mucho más rápidos cuando existen filas.

+0

En pocas palabras: agregar ROWNUM = 1 NO es solo una medida de "optimización": se debe considerar una parte importante de la redacción de cada consulta para hacer exactamente lo que debería hacer, y nada más. –

+0

¡Guau, mi primer voto! :) –

+0

¿Alguien quiere explicar lo que está mal aquí? solo para mi edificación ... –

1

Aunque me gusta el más alto votaron respuesta, en un esfuerzo por evitar excepciones, hago algo como esto:

begin 

    select count(*) 
    into ls_exists 
    from dual 
    where exists (select null from ... where ...); 

    if ls_exists = 1 then 
    do_something; 
    else 
    do_something_else; 
    end if; 

end; 

Esto le siempre devolver una sola fila con un 1 o un 0 . Sin excepciones.

Tenga en cuenta también el uso de SELECT NULL. Esto hace un par de cosas; cuando el optimizador considere que es apropiado, Oracle solo verá los índices. Si coloca columnas de tabla en la cláusula SELECT y las columnas no son parte de un índice, Oracle hará la búsqueda de índice, luego obtendrá la fila de la tabla, lo que puede ser totalmente inútil dependiendo de la consulta. Como solo está comprobando la existencia de una fila, probablemente no necesite datos reales.

SELECT NULL no devuelve ningún dato (una optimización), y la cláusula EXISTS analiza si se devuelve una fila, no los datos en la fila.

Cuestiones relacionadas