sql >> Base de Datos >  >> RDS >> PostgreSQL

SQL:cuando se trata de NOT IN y NOT EQUAL TO, ¿cuál es más eficiente y por qué?

En PostgreSQL suele haber una diferencia bastante pequeña en longitudes de lista razonables, aunque IN es mucho más limpio conceptualmente. Muy largo AND ... <> ... listas y NOT IN muy largas ambas listas funcionan terriblemente, con AND mucho peor que NOT IN .

En ambos casos, si son lo suficientemente largos como para que incluso hagas la pregunta, deberías hacer una prueba de exclusión de subconsulta o anti-unión sobre una lista de valores.

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);

o:

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;

(En las versiones modernas de Pg, ambas producirán el mismo plan de consulta de todos modos).

Si la lista de valores es lo suficientemente larga (muchas decenas de miles de elementos), el análisis de consultas puede comenzar a tener un costo significativo. En este punto, debería considerar crear un TEMPORARY tabla, COPY ing los datos para excluirlos, posiblemente creando un índice en ellos, luego usando uno de los enfoques anteriores en la tabla temporal en lugar del CTE.

Demostración:

CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;

donde exclude es la lista de valores a omitir.

Luego comparo los siguientes enfoques sobre los mismos datos con todos los resultados en milisegundos:

  • NOT IN lista:3424.596
  • AND ... lista:80173.823
  • VALUES basado en JOIN exclusión:20.727
  • VALUES exclusión de subconsulta basada:20.495
  • Basado en tablas JOIN , sin índice en la lista ex:25.183
  • Basado en tabla de subconsultas, sin índice en lista ex:23.985

... haciendo que el enfoque basado en CTE sea más de tres mil veces más rápido que el AND lista y 130 veces más rápido que el NOT IN lista.

Codifique aquí:https://gist.github.com/ringerc/5755247 (proteja sus ojos, los que siguen este enlace).

Para este tamaño de conjunto de datos, agregar un índice en la lista de exclusión no supuso ninguna diferencia.

Notas:

  • IN lista generada con SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND lista generada con SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • La exclusión de tablas basadas en subconsultas y combinaciones fue muy similar en ejecuciones repetidas.
  • El examen del plan muestra que Pg traduce NOT IN a <> ALL

Entonces... puedes ver que hay un verdaderamente enorme brecha entre ambos IN y AND listas vs hacer una combinación adecuada. Lo que me sorprendió fue lo rápido que lo hizo con un CTE usando un VALUES la lista estaba... analizando los VALUES la lista no tomó casi nada de tiempo, con un rendimiento igual o ligeramente más rápido que el enfoque de tabla en la mayoría de las pruebas.

Sería bueno si PostgreSQL pudiera reconocer automáticamente un IN ridículamente largo cláusula o cadena de AND similares condiciones y cambie a un enfoque más inteligente, como hacer una combinación hash o convertirlo implícitamente en un nodo CTE. Ahora mismo no sabe cómo hacerlo.

Véase también:

  • esta útil publicación de blog que Magnus Hagander escribió sobre el tema