2009-07-02 15 views
6

Esto es tres selecciones diferentes usando la misma sub consulta. ¿Cómo puedo usar el resultado de la subconsulta en lugar de hacer una sub consulta de nuevo?¿Cómo evitar la misma subconsulta en la selección múltiple en el oráculo?

SELECT * 
FROM Address 
WHERE address_key IN 
     (
     SELECT address_key 
     FROM person_address 
     WHERE peson_key IN (person_list) 
     ); -- person_list := '1,2,3,4' 

SELECT * 
FROM Phone 
WHERE phone_key IN 
     (
     SELECT address_key 
     FROM person_address 
     WHERE peson_key IN (person_list) 
     ); 

SELECT * 
FROM Email 
WHERE address_key IN 
     (
     SELECT address_key 
     FROM person_address 
     WHERE peson_key IN (person_list) 
     ); 

Respuesta

6

Puede crear una vista materializada para esta consulta:

CREATE MATERIALIZED VIEW v_address 
REFRESH FORCE ON COMMIT 
AS 
SELECT address_key 
FROM person_address 
WHERE person_key IN (person_list) 

, o crear una tabla temporal y rellenarla:

CREATE GLOBAL TEMPORARY TABLE tt_address (VARCHAR2(50)); 

INSERT 
INTO tt_address 
SELECT address_key 
FROM person_address 
WHERE person_key IN (person_list) 

, pero, en realidad, si el índice de su person_key, está bien reutilizar la subconsulta.

Ya que tiene consultas separadas de 3, necesita que sus valores sean visibles para ellos de una forma u otra.

Eso significa que debe almacenar estos valores en algún lugar, ya sea memoria, tablespace temporal o tablespace permanente.

Pero los valores que necesita ya están almacenados en el person_address, todo lo que necesita es recuperarlos.

El uso de la subconsulta 3 veces implicará 12 exploraciones de índices a buscar a los ROWID 's del índice en person_key y 12 mesa ROWID búsquedas a buscar address_key de la mesa. Entonces probablemente se construirá sobre ellos un HASH TABLE.

Esto es cuestión de microsegundos.

Por supuesto, la tabla temporal o una vista materializada sería un poco más eficiente, pero cambiar el tiempo de subconsulta de 100 microsegundos a 50 no vale la pena, siempre que las consultas principales tarden unos minutos.

+0

+1 o la idea de GTT, especialmente si la lógica para poblarla es más complicada o más lenta que en el ejemplo aquí. –

6

Usa la cláusula with. No volví a crear el problema de ejemplo exacto, pero cualquier número de subconsultas repetidas se puede poner en la cláusula WITH y luego se puede hacer referencia a ellas en la consulta.

WITH address_keys as (
     SELECT address_key 
     FROM person_address 
     WHERE peson_key IN (person_list) 
     ) 
Select * from table1, table2, address_keys 
where table1.address_key = address_keys.address_key 
and table2.address_key = address_keys.address_key 
+0

With no devuelve tres conjuntos de resultados como yo quiero. La vista creada con la cláusula WITH solo se puede usar en una consulta no múltiple. Me podría estar perdiendo algo. –

+0

Creo que el punto de Brian es no ejecutar tres consultas: ejecutar una consulta por UNION ALL'ing them together. Será más eficiente de esta manera también. –

+0

Por supuesto, si los tres conjuntos de resultados tienen patrones muy diferentes, entonces quizás esto no ayude :) –

4

En primer lugar creo que en la mayoría de los casos esta optimización no aporta mejoras significativas (después de la primera consulta de los bloques de datos de PERSON_ADDRESS se almacenan en caché en su mayoría en la caché del búfer y por lo tanto no se leen desde el disco duro).

Sin embargo, como estudio de un caso o por cualquier razón: Debe almacenar en caché los resultados de la consulta repetida y luego volver a utilizarlos en 3 selecciones. Esto se puede lograr mediante una tabla (temp) o una varray de estructura plsql.

Las primeras dos opciones cubren a Quassnoi en su respuesta, así que no las mencionaré. El tercero tiene la desventaja de tener que indicar el recuento máximo de filas por adelantado (y no sé exactamente qué sucede cuando declaras un varray con un límite superior de 1M o 1G elementos, incluso si solo necesitas 1k).

--creating db object to hold the data - maximum of 1000 items allowed. 
--I assume that key is number(10,0). 
create type t_address_keys is varray (1000) of number (10,0); 

declare 
    la_address_keys t_address_keys; --declare cache variable 
begin 

--cache keys 
SELECT address_key 
bulk collect into la_address_keys 
     FROM person_address 
     WHERE peson_key IN (person_list); 

SELECT * 
into ... 
FROM Address 
WHERE address_key IN table(la_address_keys); 

SELECT * 
into ... 
FROM Phone 
WHERE address_key IN table(la_address_keys); 

SELECT * 
into ... 
FROM email 
WHERE address_key IN table(la_address_keys); 

end; 
/
Cuestiones relacionadas