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.596AND ...
lista:80173.823VALUES
basado enJOIN
exclusión:20.727VALUES
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 conSELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
AND
lista generada conSELECT 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