2012-01-05 24 views
6

Necesito devolver todos los valores de colA que no están en colB de mytable. Estoy usando:Manera eficiente de seleccionar todos los valores de una columna no en otra columna

SELECT DISTINCT(colA) FROM mytable WHERE colA NOT IN (SELECT colB FROM mytable) 

Está trabajando sin embargo, la búsqueda está tardando demasiado tiempo en completarse.

¿Hay una manera más eficiente de hacer esto?

+0

'NOT IN' se ralentiza a medida que crece el tamaño del conjunto, y a menudo hay un límite en la cantidad de filas en la cláusula' NOT IN'. Aparte de los conjuntos de resultados pequeños, he encontrado que es mejor usar otros medios para obtener la diferencia entre dos conjuntos de resultados. – Paul

+0

Cuando habla de rendimiento, debe nombrar su RDBMS u obtener respuestas poco óptimas. –

Respuesta

10

En SQL estándar hay sin paréntesis en DISTINCT colA. DISTINCT no es una función.

SELECT DISTINCT colA 
FROM mytable 
WHERE colA NOT IN (SELECT DISTINCT colB FROM mytable); 

Se agregó DISTINCT a la sub-selección también. Si tiene muchos duplicados, podría acelerar la consulta.

Un CTE puede ser más rápido, dependiendo de su DBMS. Yo, además, demostrar LEFT JOIN como alternativa para excluir los valores en valB, y una forma alternativa de obtener valores distintos con GROUP BY:

WITH x AS (SELECT colB FROM mytable GROUP BY colB) 
SELECT m.colA 
FROM mytable m 
LEFT JOIN x ON x.colB = m.colA 
WHERE x.colB IS NULL 
GROUP BY m.colA; 

O simplificado aún más, y con una subconsulta normal (probablemente el más rápido):

SELECT DISTINCT m.colA 
FROM mytable m 
LEFT JOIN mytable x ON x.colB = m.colA 
WHERE x.colB IS NULL; 

Hay básicamente 4 técnicas para excluir filas con claves presentes en otro (o el mismo) tabla:

El factor decisivo para la velocidad será índices. Debe tener índices en colA y colB para que esta consulta sea rápida.

+0

Gracias, lo he intentado: 'SELECCIONAR DISTINCT m1.colA FROM mytable m1 IZQUIERDA UNIR mytabletable m2 ON (m1.colA = m2.colB) DONDE m2.colA ES NULL ORDER BY m1.colA ASC' y es varios órdenes de magnitud más rápido y parece estar funcionando - ¿es esto equivalente al código que publiqué en la pregunta? Es mucho más rápido que sospeche que podría haberme perdido algo. – Flash

+0

@Andrew: Lo siento, tienes un error tipográfico en tu consulta. Debe ser 'WHERE m2.colB IS NULL'. La consulta (corregida) puede ser más rápida con 'LEFT JOIN (SELECT DISTINCT colB FROM mytable) m2 ON m2.colB = m1.colA' ** if ** hay muchos valores duplicados para' colB'. –

+0

@Andrew: 'm2.colA' siempre es' NULL' si 'm2.colB' es NULL aquí, pero' m2.colA' puede ser NULL aunque 'm2.colB' no lo sea. Entonces la forma correcta (¡y más rápida!) Aquí es: 'DONDE m2.colB ES NULO'. ** Si ** 'colA' se define NO NULO, entonces su consulta anterior es correcta. –

6

Puede utilizar exists:

select distinct 
    colA 
from 
    mytable m1 
where 
    not exists (select 1 from mytable m2 where m2.colB = m1.colA) 

exists hace un semi-unen para igualar rápidamente los valores. not in completa el conjunto de resultados completo y luego hace un or en él. exists suele ser más rápido para los valores en tablas.

+0

¿Puede explicar cómo funciona esta consulta? – Flash

+0

@Andrew - ¡Claro! Dice, agarra el distinto 'colA's donde no hay fila de' mitabla' que 'colB' es igual a' colA'. – Eric

0

Puede utilizar el operador EXCEPT que efectivamente diffs dos consultas SELECT. EXCEPT DISTINCT devolverá solo valores únicos. El operador MINUS de Oracle es equivalente a EXCEPT DISTINCT.

Cuestiones relacionadas