2011-05-23 8 views
7

que tienen la siguiente declaración para encontrar nombres inequívocos en mis datos (~ 1 millón de entradas):¿Por qué el costo de SQL explota con el simple "o"?

select Prename, Surname from person p1 
where Prename is not null and Surname is not null 
and not exists (
    select * from person p2 where (p1.Surname = p2.Surname OR p1.Surname = p2.Altname) 
    and p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id 
) and inv_date IS NULL 

Oracle muestra un enorme costo de 1477315000 y ejecución no termina después de 5 minutos. Basta con dividir el quirófano en una propia existe el rendimiento aumenta subcláusula a 0,5 s y costos a 45000:

select Prename, Surname from person p1 
where Prename is not null and Surname is not null 
and not exists (
    select * from person p2 where p1.Surname = p2.Surname and 
    p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id 
) and not exists (
    select * from person p2 where p1.Surname = p2.Altname and 
    p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id 
) and inv_date IS NULL 

No es mi pregunta para ajustar esto a lo mejor, ya que es sólo una consulta rara vez ejecutado, y yo sé que CONTACT está superando cualquier índice, pero me pregunto de dónde viene este alto costo. Ambas consultas parecen ser semánticamente equivalentes a mí.

Respuesta

6

La respuesta está en el PLAN DE EXPLICACIÓN para sus consultas. Pueden ser semánticamente equivalentes, pero el plan de ejecución entre bastidores para sus consultas es muy diferente.

EXISTS funciona de forma diferente a un JOIN y, esencialmente, su declaración de filtro OR es lo que une a las tablas.

No se produce JOIN en la segunda consulta, ya que solo está recuperando registros de una tabla.

+3

+1 - Para elaborar, 'EXISTS' cortocircuitos y' OR' no (al menos en SQL Server, supongo que Oracle es similar). Al incluir el 'OR 'en el submenú' EXISTS' comprueba ambas opciones cada vez. Separar significa que solo verifica el segundo si el primero es falso. – JNK

+2

+1 - Plan de ejecución 1: el filtro no existe (...) 1477315000 | Persona de acceso a la tabla por índice ROWID 13863 | Persona de acceso a la tabla por índice ROWID 4019; Plan 2 es enorme y utiliza dos combinaciones de hash – stracktracer

+0

Aceptado como.Parece que sobreestimé el analizador de consultas de Oracle. – stracktracer

2

Los resultados de sus dos consultas pueden ser semánticamente equivalentes, pero la ejecución no es operativamente equivalente. Su segundo ejemplo nunca utiliza un operador OR para combinar predicados. Todos sus predicados en el segundo ejemplo se combinan usando un AND.

El rendimiento es mejor porque, si el primer predicado que se combina con un AND no se evalúa como verdadero, el segundo (o cualquier otro predicado) se omite, (no se evalúa). Si utilizó un OR, ambos (o todos) los predicados tendrían que evaluarse con frecuencia, lo que ralentizaría su consulta. (Los predicados ORed se marcan hasta que se evalúa como verdadero.)

+3

Con semánticamente equivalente, me refería a producir el mismo conjunto de resultados, que ahora creo que lo hacen ... – stracktracer

+0

@stacktracer: buen punto. Voy a modificar mi respuesta con algo así como 'operacionalmente equivalente'. Aunque no asumiría la equivalencia semántica en diferentes consultas. Pero creo que no solo eres más rápido sino también más seguro con tu segundo ejemplo al omitir OR. Los quirófanos pueden causar estragos con los resultados. –

1

Consideraría probar la consulta reescrita como a continuación ... Hacer una unión directa de uno a otro en los criterios que "Califica" lo que se considera una coincidencia. .. a continuación, en la cláusula WHERE, a la basura si no llegar a un partido

select 
     p1.Prename, 
     p1.Surname 
    from 
     person p1 
     join person p2 
      on p1.ID <> p2.ID 
      and ( p1.Surname = p2.Surname 
       or p1.SurName = p2.AltName) 
      and p2.PreName like concat(concat('%', p1.Prename), '%') 
    where 
      p1.PreName is not null 
     and p1.SurName is not null 
     and p1.Inv_date is null 
     and p2.id is null 

por sus comentarios, pero por lo que parece que estás buscando ... no, no hacer una combinación externa izquierda ... Si está buscando nombres que son ALIKE que desea PURGAR (independientemente de cómo lo manejará), solo desea PREQUALIFICAR aquellos registros que TENGAN UN PARTIDO a través de la auto-unión (por lo tanto, unión normal). Si tiene un nombre que NO TIENE un nombre similar, es probable que desee dejarlo en paz ... por lo tanto, se quedará automáticamente FUERA del conjunto de resultados.

Ahora, la cláusula WHERE se activa ... Tienes una persona válida a la izquierda ... que TIENE a una persona a la derecha ... Estos SON los duplicados ... así que tienes la coincidencia, ahora tirando en la lógica "p2.ID IS NULL" crea el mismo resultado que NOT EXIST dando los resultados finales.

Devuelvo mi consulta a una "combinación" normal.

+0

¿Esto no me da los nombres ambiguos? – stracktracer

+0

Corregí la consulta para reflejar que te refieres a UNA UNIÓN EXTREMA IZQUIERDA, no una UNIÓN. El uso de un JOIN probablemente no arrojará resultados si ningún ID es nulo. – Benoit

+1

@stracktracer: utilizando LEFT JOIN b WHERE b.id IS NULL es una forma inteligente de hacer un NO EXISTE. – Benoit

Cuestiones relacionadas