2011-08-09 10 views
6

Estoy trabajando en la generación de informes para datos contenidos en una gran base de datos de Access (~ 500 mb después de la reparación compacta &), y estoy teniendo problemas con una lenta subconsulta .Subconsultas muy lentas al usar "NOT IN"

La base de datos tiene una gran tabla que contiene un registro de la compra de cada cliente. Aquí hay una consulta simple que encuentra clientes que compraron un widget azul. Se completa en unos pocos segundos y devuelve unos diez mil registros.

SELECT DISTINCT CustomerId 
FROM ProductSales 
WHERE Product = 'BLUE' 

Aquí hay una pregunta que trata de encontrar clientes que han comprado un widget azul, pero no es un widget rojo. Lleva alrededor de una hora correr.

SELECT DISTINCT CustomerId FROM ProductSales 
WHERE Product = 'BLUE' 
AND CustomerId NOT IN (
    SELECT CustomerId 
    FROM ProductSales 
    WHERE Product = 'RED' 
) 

¿Hay alguna manera de refactorizar la segunda consulta para que tome unos minutos en lugar de una hora?

+0

Supongo que el campo CustomerId tiene índices en ambas tablas? –

+0

Ha intentado SELECCIONAR DISTINCT CustomerId FROM ProductSales WHERE Producto = 'AZUL' menos SELECCIONAR ID de cliente DEVENTAS DE PRODUCTO DONDE Producto = 'ROJO'. He visto casos en los que realmente aceleró la consulta, pero YMMV –

+0

@Marc B: aquí solo hay una tabla, pero CustomerId está indexada en ella. – James

Respuesta

10

acceso motor de base de datos no se puede usar un índice para Not In, por lo que está destinado a ser lento. Con un índice en CustomerId, esta consulta debería ser mucho más rápida porque el motor de db puede usar el índice.

SELECT DISTINCT blue.CustomerId 
FROM 
    ProductSales AS blue 
    LEFT JOIN 
     (
      SELECT CustomerId 
      FROM ProductSales 
      WHERE Product = 'RED' 
     ) AS red 
    ON blue.CustomerId = red.CustomerId 
WHERE 
     blue.Product = 'BLUE' 
    AND red.CustomerId Is Null; 

Probablemente se podría también intentar un enfoque Not Exists, pero el uso de índice no está garantizada. Además, consulte el comentario siguiente de David Fenton, que analiza el impacto en el rendimiento con más detalle.

+1

Woah. De 60 minutos a aproximadamente un segundo. Eso es como 3600 veces más rápido que la versión anterior. ¡Gracias! :-) Para referencia futura, ¿hay una lista de operaciones SQL? Access no usa el índice para alguna parte? – James

+1

Probablemente haya una lista en alguna parte, pero no sé dónde. :-) Si quieres ir al grano en esto, Google Jet ShowPlan ... que te dirá con autoridad cómo/si el motor db está usando índices con tu consulta. Puede encontrar una explicación detallada del rendimiento de las consultas aquí: http://msdn.microsoft.com/en-us/library/aa188211(office.10).aspx – HansUp

+1

No es que NO HAYA Y NO EXISTE nunca use los índices, es que no puedes predecir cuándo lo harán y cuándo no. Cada vez que pueda rediseñar una subconsulta NOT IN/EXISTS en un JOIN, probablemente mejore el rendimiento. Sin embargo, todo depende de la capacidad de edición del conjunto de registros resultante: algunas subconsultas utilizadas en un JOIN harán que la consulta no sea actualizable. –

0

Agregue un índice, por supuesto, si no tiene uno. Si eso es un problema, es probable que haya muchos clientes con pedidos de algo que no sea RED, pero no tantos con AZUL; esta consulta (no probada) intenta arreglar eso.

SELECT DISTINCT CustomerId FROM ProductSales 
LEFT JOIN (
    SELECT DISTINCT CustomerId cid FROM ProductSales 
    LEFT JOIN (
    SELECT DISTINCT CustomerId 
    FROM ProductSales 
    WHERE Product = 'BLUE' 
) foo ON CustomerId = cid 
    WHERE Product = 'RED' 
) bar USING (CustomerId) 
WHERE cid IS NULL 
Cuestiones relacionadas